stmllr.net

Prevent SQL injection in TypoScript when using strings from GET parameters

by on stmllr.net

Whenever you are using GET/POST parameters in SQL queries, you have to face the risk of running into SQL injection. The golden rule here is not to trust user parameters, but always properly check and escape values. This article introduces a TYPO3 extension, which helps to sanitize parameters by extending TypoScript stdWrap. PLEASE ALSO READ THE COMMENTS ON THIS ARTICLE FOR UPDATES IN THE TYPO3 CORE TO TACKLE THIS ISSUE!

Update

Please have a look at the comments on this article to read about updates and changes done in the TYPO3 core to tackle this problem.

I recommend to use markers for inserting parameters into queries as explained in https://docs.typo3.org/typo3cms/TyposcriptReference/Functions/Select/Index.html.

Introduction

As I have already shown in an article have a year ago, TypoScript is not save against SQL injections. I introduced a case, where a numerical GET parameter (a uid as integer) is used in the WHERE clause of a TypoScript CONTENT cObject. But the parameter could be misused to inject malicious SQL into the WHERE clause. The solution was to parse the GET parameter through the stdWrap function intval. It ensured that only integer values were passed to the WHERE clause.

The posting you are now about to read is meant as a follow-up to that article. It demonstrates a more advanced case: using a string of a GET parameter inside an SQL query of CONTENT.

Example code

Imagine you offer some search function to your website visitors. The search term is send as a GET parameter and TypoScript compares the term with the header of the content elements of a page. All content element of a page which is matching this comparison are shown. The page content is:

backend view of content elements

We use the TypoScript cObject CONTENT to output all matching content elements of a page. The search term is inserted into the WHERE part of the SQL query:

page.10 = CONTENT
page.10 {
  table = tt_content
  select {
    pidInList = 35
    andWhere.cObject = TEXT
    andWhere.cObject {
      data = GPvar:tx_myext|header
      wrap = header=”|”   
}
  }
}

Result

If the URL contains the GET parameter tx_myext[header] it uses the given value in the SQL WHERE clause. For example, try this URL to get all content elements which use the term My products as header:

http ://www.example.com/index.php?id=123&tx_myext[header]=My products

That will result in a SQL clause like this:

SELECT * FROM tt_content ... WHERE ... AND header=”My products” ...

Have a look at the output of this TypoScript object. In the frontend, you'll see this:

Example output without SQL injection

SQL injection vulnerability

With the TypoScript above, your website is vulnerable to SQL injections. Imagine a visitor uses the following malicious URL for SQL injection:

http ://www.example.com/index.php?id=123&tx_myext[header]=abc" OR 1=1 OR 1="1

This will result in a SQL clause:

SELECT * FROM tt_content ... WHERE ... AND header=”abc” OR 1=1 OR 1=”1” ...

In the frontend, you'll see something like this:

Example output with SQL injection

The website shows all content elements, no matter if they are deleted or hidden!

Solution: quoting and escaping the string

To prevent bad guys from injecting SQL with the search term, you need to quote the string and escape possible control characters. Unfortunately, there's no function in stdWrap to do that.

I have recently released the "quote functions for stdWrap" extension (sm_stdwrapquote) to TER. This extension adds two functions to TypoScript stdWrap:

  • quoteStr to escape special values
  • fullQuoteStr to escape special values and add quotes

It implements the functionality of t3lib_DB->quoteStr() and t3lib_DB->fullQuoteStr() to stdWrap. This helps you sanitize and safely use GET/POST strings in TypoScript CONTENT objects without too much hassle.

Example code with protection against SQL injection

The extension provides a simple method to protect your TypoScript from SQL injection. You simply add fullQuoteStr to the andWhere part of your CONTENT object. It can be used like a regular function of stdWrap. When using fullQuoteStr, you also don't need quotes in the wrap:

page.10 = CONTENT
page.10 {
  table = tt_content
  select {
    pidInList = 35
    andWhere.cObject = TEXT
    andWhere.cObject {
      data = GPvar:tx_myext|header
      fullQuoteStr = tt_content
      wrap = header=|

    }
  }
}

The result is that SQL injection will not work anymore. When using the malicious URL, the frontend won't show any content elements at all. fullQuoteStr escapes all quotes inside the GET parameter and quotes the whole string. Data can be safely used with fullQuoteStr inside an SQL SELECT query of CONTENT.

For some more information, read the manual which is shiped with the extension.

Conclusion

You always have to protect your SQL queries of TypoScript cObject CONTENT against SQL injection. Since stdWrap does not ship sufficient functions to sanitize user parameters, use the fullQuoteStr function of the extension sm_stdwrapquote.

Future implementation in TYPO3 core

There's an RFC in the TYPO3 core team list to implement these functions into the core. Unfortunately this did not happen with version 4.3. But I try to push this into 4.4. If you like it, go to the core list and vote for feature #12094.

Related articles

Tags

Comments

  1. Alex

    Hi!

    I created a video tutorial about SQL injection.
    Take a look:

    http://www.webmastervideoschool.com/blog_item.php?id=7

  2. Steffen

    Some "news" on this topic:

    In TYPO 4.4, a new concept for building queries has been introduced to TS cObject CONTENT.
    GPvars fetched within the new property "markers" are properly escaped and quoted to prevent SQL injection. They can be inserted into the query with classic ###YOUR_MARKER_TITLE### markers.

    If you use this concept, you don't need the sm_stdwrapquote extension anymore.

    The following example is taken from the pending documentation [1] for TSref. For implementation details, have a look at the bug report #13940 in mantis [2].

    page.60 = CONTENT
    page.60 {
      table = tt_content
      select {
        pidInList = 73
        where = header != ###whatever###
        orderBy = ###sortfield###
        markers {
          whatever.data = GP:first
          sortfield.value = sor
          sortfield.wrap = |ting
        }
      }
    }

    [1] http://wiki.typo3.org/Documentation_changes_in_4.4_and_4.5
    #13940:_Added_marker_in_CONTENT_object_.5Bclosed_to_Steffen_Kamper.5D.5B4.4.5D_.5BHASPATCH.5D


    [2] http://bugs.typo3.org/view.php?id=13940