The form-variable filter allows you to specify datatypes for url variables. For example, you could specify that user_id is always an integer, and if anyone passes in anything else, the page will throw an error.
It's possible to restrict filters to certain url patterns, which is necessary if you use the same variable name in different modules. For example, you could restrict $my_key to be an integer under /module_one/* and a character string when used under /module_two/*.
The filters allows you to specify wild-card patterns, so you can specify rules like 'bboard_id is a character string, but all other *_id variables are integers.'
ad_set_typed_form_variable_filter url_pattern list_of_variables_to_filter
list_of_variables_to_filter is a list of lists that specifies (1) the variable to check, and (2) the datatype that we expect.
Examples:
Under /bboard/*, topic_id must an integer, and msg_id must be a 'word' (a string that contains only letters, numbers, dashes, and underscores.)
ad_set_typed_form_variable_filter /bboard/* {msg_id word} {topic_id integer}The default datatype is 'integer', which would allow you to write the spec above as
ad_set_typed_form_variable_filter /bboard/* {msg_id word} topic_id
integer number Any real number. We check its validity by asking Tcl to do math with it. word A string that contains only letters, numbers, dashes, and underscores. noquote A string that contains no single quotes. Use this when a string is allowed to have spaces, but not quotes. (Dates fit into this category.) integerlist A string that contains only numbers, spaces, and commas (but no negative numbers, because this would allow people to sneak in math.) safefilename A filename that doesn't contain '..' in it. dirname A string that doesn't have / or \ in it. fail The check should fail regardless of value. nocheck The check should pass regardless of value The nocheck operator is useful if you want to specify blanket rules, then create a few exceptions. For example, if all the variables on the site that end with _key were integers, except for $long_key, which is a string that could contain any letter (including quotes), you could write the spec
ad_set_typed_form_variable_filter /* {long_key nocheck} *_keyThe fail operator is used when you want to turn off access to a page by not allowing any url variables to be set.
ad_set_typed_form_variable_filter /* {user_id_to_delete fail}
Normal case:
select .. from users where user_id = 5Nasty case:
select .. from users where user_id = (select user_id where email = 'dvr@arsdigita.com')This bug can be patched using the integer data type.
Normal case:
select .. from bboard where msg_id = '000001'Nasty case:
select .. from bboard where msg_id = '' || (select 'other' from dual) || ''This bug can be patched using the noquote data type (because any variable that can be used without QQing it should never have quotes in it).
set user_id [ad_get_user_id] set_form_variablesthen a user can masquerade as another user by passing user_id as a form variable.
This type of bug can be fixed using fail, e.g., make the page throw an error if someone passes in $user_id.
(Read the documentation for ad_set_typed_form_variable_filter to learn how to add special-case functions like this.)
There's one file, z-security-patch.tcl, which contains patches to most of the known security bugs in a standard ACS installation (as of midnight, July 3). Put this in your /tcl directory. It includes
- a revised ad_page_variables
- a revised set_form_variables
- a revised set_the_usual_form_variables
- The new function check_for_form_variable_naughtiness, which gets called from within the three functions above, and checks whether the variables being created are of the right type, as well as running a few other sanity checks (e.g., not allowing users to pass in QQ variables).
- The new function ad_set_typed_form_variable_filter
- The calls to ad_set_typed_form_variable_filter that will protect you against most of the 140 bugs that were found as of midnight of July 3.
To secure custom code, you'll need to figure out which variables in your code might be dangerous. Here are a few queries that might help.
All your system's primary keys:
select c1.table_name, c1.column_name from all_cons_columns c1, all_constraints c2 where c1.constraint_name = c2.constraint_name and constraint_type='P' order by table_name, column_nameAll the columns that end with _id.
select table_name, column_name, data_type from user_tab_columns where column_name like '%/_ID' escape '/' order by table_name, column_nameAll the columns that end with _id that aren't numbers.
select table_name, column_name, data_type from user_tab_columns where data_type != 'NUMBER' and column_name like '%/_ID' escape '/' order by table_name, column_nameAll the columns that are numbers
select table_name, column_name, data_type from user_tab_columns where data_type = 'NUMBER' order by table_name, column_name(The query that you really want is "show me all primary keys which aren't integers," but the three-table join on system tables never returns.)