Index: openacs-4/packages/acs-core-docs/www/security-design.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-core-docs/www/security-design.html,v diff -u -r1.28.2.1 -r1.28.2.2 --- openacs-4/packages/acs-core-docs/www/security-design.html 14 Jan 2007 04:20:11 -0000 1.28.2.1 +++ openacs-4/packages/acs-core-docs/www/security-design.html 14 Jul 2007 12:34:48 -0000 1.28.2.2 @@ -1,8 +1,7 @@ - -
By Richard Li and Archit Shah
+By Richard Li and Archit Shah
OpenACS docs are written by the named authors, and may be edited by OpenACS documentation staff. -This document explains security model design for OpenACS 4. The security system with the OpenACS core must authenticate users in both secure and insecure environments. In addition, this subsystem provides sessions on top of the @@ -18,15 +17,15 @@
SSL with server authentication: SSL v3
SSL provides the client with a guarantee that the server is actually the server it is advertised as being. It also provides a secure transport. -
A session is defined as a series of clicks in which no two clicks are separated by more than some constant. This constant is the parameter SessionTimeout. Using the expiration time on the signatures of the signed cookies, we can verify when the cookie was issued and determine if two requests are part of the same session. It is important to note that the expiration time set in the cookie protocol is not trusted. Only the time inserted by the signed cookie mechanism is trusted. -
Two levels of access can be granted: insecure and secure. This grant lasts for the remainder of the particular session. Secure authentication tokens are only issued over secured connections. @@ -40,86 +39,86 @@ password can be sniffed from the system, after which the sniffer can apply for a secure authentication token. However, the basic architecture here lays the foundation for a secure system and can be easily adapted to a more secure -authentication system by forcing all logins to occur over HTTPS.
The authentication system issues up to four signed cookies (see below), -with each cookie serving a different purpose. These cookies are:
name | value | max-age | secure? |
ad_session_id | session_id,user_id | SessionTimeout | no |
ad_user_login | user_id | Infinity | no |
ad_user_login_secure | user_id,random | Infinity | yes |
ad_secure_token | session_id,user_id,random | SessionLifetime | yes |
ad_session_id
reissued on any hit separated by more than SessionRenew seconds from the +authentication system by forcing all logins to occur over HTTPS.
The authentication system issues up to four signed cookies (see below), +with each cookie serving a different purpose. These cookies are:
name | value | max-age | secure? |
ad_session_id | session_id,user_id | SessionTimeout | no |
ad_user_login | user_id | Infinity | no |
ad_user_login_secure | user_id,random | Infinity | yes |
ad_secure_token | session_id,user_id,random | SessionLifetime | yes |
ad_session_id
reissued on any hit separated by more than SessionRenew seconds from the previous hit that received a cookie
is valid only for SessionTimeout seconds
is the canonical source for the session ID in ad_conn
ad_user_login
is used for permanent logins
ad_user_login_secure
is used for permanent secure logins
contains random garbage (ns_time) to prevent attack against the secure hash
ad_secure_token
is a session-level cookie from the browser's standpoint
its signature expires in SessionLifetime seconds
contains random garbage (ns_time) to prevent attack against the secure -hash
user_id is extraneous
The Tcl function (sec_handler
) is called by the request
processor to authenticate the user. It first checks the
-ad_session_id cookie. If there is no valid session in progress,
-a new session is created with sec_setup_session. If the user
-has permanent login cookies (ad_user_login and
-ad_user_login_secure), then they are looked at to determine what
+ad_session_id
cookie. If there is no valid session in progress,
+a new session is created with sec_setup_session
. If the user
+has permanent login cookies (ad_user_login
and
+ad_user_login_secure
), then they are looked at to determine what
user the session should be authorized as. Which cookie is examined is
determined by whether or not the request is on a secure connection. If
neither cookie is present, then a session is created without any
-authentication. If the ad_session_id cookie is valid, the
-user_id and session_id are pulled from it and put into ad_conn.
Secure connections are authenticated slightly differently. The function
-ad_secure_conn_p is used to determine whether or not the URL
+authentication. If the ad_session_id
cookie is valid, the
+user_id and session_id are pulled from it and put into ad_conn.
Secure connections are authenticated slightly differently. The function
+ad_secure_conn_p
is used to determine whether or not the URL
being accessed is requires a secure login. The function simply checks if the
-location begins with "https". (This is safe because the location is
-set during the server initialization.)
If secure authentication is required, the ad_secure_token +location begins with "https". (This is safe because the location is +set during the server initialization.)
If secure authentication is required, the ad_secure_token
cookie is checked to make sure its data matches the data stored in
-ad_session_id. This is true for all pages except those that are
+ad_session_id
. This is true for all pages except those that are
part of the login process. On these pages, the user can not yet have received
-the appropriate ad_secure_token cookie, so no check against it
+the appropriate ad_secure_token
cookie, so no check against it
is performed. The set of pages that skip that processing are determined by
-determined by ad_login_page. Since the
-ad_secure_token cookie is a session cookie, it is deleted by the
+determined by ad_login_page
. Since the
+ad_secure_token
cookie is a session cookie, it is deleted by the
browser when the browser exits. Since an attacker could conceivably store the
secure cookie in a replay attack (since expiration date is not validated),
the data in the secure cookie is never used to set any data in ad_conn;
user_id and session_id is set from the ad_session_id cookie.
It is important to note that the integrity of secure authentication rests -on the two Tcl function ad_secure_conn_p and -ad_login_page. If ad_secure_conn_p is false, secure -authentication is not required. If ad_login_page is false, -secure authentication is not required.
The Tcl function ad_user_login does two things. First it
+on the two Tcl function ad_secure_conn_p
and
+ad_login_page
. If ad_secure_conn_p
is false, secure
+authentication is not required. If ad_login_page
is false,
+secure authentication is not required.
The Tcl function ad_user_login
does two things. First it
performs the appropriate manipulation of the permanent login cookies, and
then it updates the current session to reflect the new user_id. The
manipulation of the permanent login cookies is based on 3 factors:
previous login: other user, same user
permanent: was a permanent login requested?
secure: is this a secure connection?
Both the secure and insecure permanent login cookie can have one of three actions taken on it: -
set: cookie with no expiration is set
delete: set to "" with max age of 0, so it is expired +
set: cookie with no expiration is set
delete: set to "" with max age of 0, so it is expired immediately
nothing: if the cookie is present, it remains
The current state of the permanent login cookies is not taken into account when determining the appropriate action. -
previous login state | permanent login requested | secure connection | action on insecure | action on secure |
other | y | y | set | set |
same | y | y | set | set |
other | y | n | set | delete |
same | y | n | set | nothing |
same | n | y | nothing | delete |
other | n | y | delete | delete |
other | n | n | delete | delete |
same | n | n | delete | delete |
ad_user_login -callssec_setup_session which actually calls -sec_generate_session_id_cookie to generate the +
previous login state | permanent login requested | secure connection | action on insecure | action on secure |
other | y | y | set | set |
same | y | y | set | set |
other | y | n | set | delete |
same | y | n | set | nothing |
same | n | y | nothing | delete |
other | n | y | delete | delete |
other | n | n | delete | delete |
same | n | n | delete | delete |
ad_user_login
+callssec_setup_session
which actually calls
+sec_generate_session_id_cookie
to generate the
new cookie with refer to the appropriate user_id. If the connection is secure
-the ad_secure_token cookie is generated by a
-call to sec_generate_secure_token_cookie. This
+the ad_secure_token
cookie is generated by a
+call to sec_generate_secure_token_cookie
. This
function is only called from
-sec_setup_session. Only
-sec_handler and
-sec_setup_session call
-sec_generate_session_id_cookie.
+sec_setup_session
. Only
+sec_handler
and
+sec_setup_session
call
+sec_generate_session_id_cookie
.
-
ad_user_logout logs the user out by deleting all 4 cookies -that are used by the authentication system.
The creation and setup of sessions is handled in -sec_setup_session, which is called either to -create a new session from sec_handler or from -ad_user_login when there is a change in +
ad_user_logout
logs the user out by deleting all 4 cookies
+that are used by the authentication system.
The creation and setup of sessions is handled in
+sec_setup_session
, which is called either to
+create a new session from sec_handler
or from
+ad_user_login
when there is a change in
authorization level. The session management code must do two things: insure that
session-level data does not float between users, and update the users table
-which has columns for n_sessions,
-last_visit, and
-second_to_last_visit.
If there is no session already setup on this hit, a new session is
-created. This happens when sec_setup_session is
-called from sec_handler. If the login is from a
+which has columns for n_sessions
,
+last_visit
, and
+second_to_last_visit
.
If there is no session already setup on this hit, a new session is
+created. This happens when sec_setup_session
is
+called from sec_handler
. If the login is from a
user to another user, a new session is created, otherwise, the current session
is continued, simply with a higher authorization state. This allows for data
associated with a session to be carried over when a user logs in.
The users table is updated by
-sec_update_user_session_info which is called
+sec_update_user_session_info
which is called
when an existing session is assigned a non-zero user_id, or when a session is
-created with a non-zero user_id.
ad_user_login
assumes a password check has already been
performed (this will change in the future). The actual check is done by
-ad_check_password. The database stores a salt and a hash of the
+ad_check_password
. The database stores a salt and a hash of the
password concatenated with the salt. Updating the password
-(ad_change_password) simply requires getting a new salt
+(ad_change_password
) simply requires getting a new salt
(ns_time) concatenating and rehashing. Both the salt and the hashed password
-field are updated.
A session is labeled by a session_id sequence. Creating a session merely +field are updated.
A session is labeled by a session_id sequence. Creating a session merely
requires incrementing the session_id sequence. We do two things to improve the
performance of this process. First, sequence values are precomputed and cached
in the Oracle SGA. In addition, sequence values are incremented by 100 with each
@@ -128,41 +127,41 @@
command per thread. This minimizes lock contention for the session ID sequence
and also minimizes the number of DB requests, since each thread can allocate 100
sessions before requiring another DB hit. This cache works by keeping two
-counters: tcl_max_value and
-tcl_current_sequence_id. When
-tcl_current_sequence_id is greater than
-tcl_max_value a new value is requested from the
-db and tcl_max_value is incremented by
+counters: tcl_max_value
and
+tcl_current_sequence_id
. When
+tcl_current_sequence_id
is greater than
+tcl_max_value
a new value is requested from the
+db and tcl_max_value
is incremented by
100. This is done on a per-thread basis so that no locking is required.
In addition, two procedures are dynamically generated at startup in
-security-init.tcl. These two procedures use
-ad_parameter to obtain the constant value of a given parameter;
+security-init.tcl
. These two procedures use
+ad_parameter
to obtain the constant value of a given parameter;
these values are used to dynamically generate a procedure that returns a
constant. This approach avoids (relatively) expensive calls to
-ad_parameter in sec_handler. The impact of this
+ad_parameter
in sec_handler
. The impact of this
approach is that these parameters cannot be dynamically changed at runtime
-and require a server restart.
Session properties are stored in a single table that maps session IDs to
named session properties and values. This table is periodically purged. For
maximum performance, the table is created with nologging turned on and new
extents are allocated in 50MB increments to reduce fragmentation. This table
-is swept periodically by sec_sweep_session which removes
+is swept periodically by sec_sweep_session
which removes
sessions whose first hit was more than SessionLifetime seconds (1 week by
default) ago. Session properties are removed through that same process with
cascading delete.
-
Session properties can be set as secure. In this case, -ad_set_client_property will fail if the connection is not -secure. ad_get_client_property will behave as if the property -had not been set if the property was not set securely.
Session properties can be set as secure. In this case,
+ad_set_client_property
will fail if the connection is not
+secure. ad_get_client_property
will behave as if the property
+had not been set if the property was not set securely.
Signed cookies are implemented using the generic secure digital signature mechanism. This mechanism guarantees that the user can not tamper with (or construct a value of his choice) without detection. In addition, it provides the optional facility of timing out the signature so it is valid for only a certain period of time. This works by simply including an expiration time as part of the value that is signed. -
The signature produced by ad_sign is the Tcl list of -token_id,expire_time,hash, where hash = +
The signature produced by ad_sign
is the Tcl list of
+token_id,expire_time,hash
, where hash =
SHA1(value,token_id,expire_time,secret_token). The secret_token is a forty
character randomly generated string that is never sent to any user agent. The
scheme consists of one table:
@@ -174,7 +173,7 @@ token_timestamp sysdate ); -
ad_verify_signature takes a value and a signature and +
ad_verify_signature
takes a value and a signature and
verifies that the signature was generated using that value. It works simply
by taking the token_id and expire_time from the signature, and regenerating
the hash using the supplied value and the secret_token corresponding to the
@@ -186,111 +185,111 @@
signature, RFC 2109 specifies an optional max age that is returned to the
client. For most cookies, this max age matches the expiration date of the
cookie's signature. The standard specifies that when the max age is not
-included, the cookie should be "discarded when the user agent
-exits." Because we can not trust the client to do this, we must specify
+included, the cookie should be "discarded when the user agent
+exits." Because we can not trust the client to do this, we must specify
a timeout for the signature. The SessionLifetime parameter is used for this
purpose, as it represents the maximum possible lifetime of a single
-session.
RFC 2109 specifies this optional "secure" parameter which -mandates that the user-agent use "secure means" to contact the +session.
RFC 2109 specifies this optional "secure" parameter which +mandates that the user-agent use "secure means" to contact the server when transmitting the cookie. If a secure cookie is returned to the client over https, then the cookie will never be transmitted over insecure -means.
Performance is a key goal of this implementation of signed cookies. To +means.
Performance is a key goal of this implementation of signed cookies. To
maximize performance, we will use the following architecture. At the lowest
-level, we will use the secret_tokens table as the canonical set
+level, we will use the secret_tokens
table as the canonical set
of secret tokens. This table is necessary for multiple servers to maintain
the same set of secret tokens. At server startup, a random subset of these
secret tokens will be loaded into an ns_cache called
-secret_tokens. When a new signed cookie is requested, a random
+secret_tokens
. When a new signed cookie is requested, a random
token_id is returned out of the entire set of cached token_ids. In addition,
a thread-persistent cache called tcl_secret_tokens is maintained on a
per-thread basis.
Thus, the L2 ns_cache functions as a server-wide LRU cache that has a -minimum of 100 tokens in it. The cache has a dual purpose:
LRU cache Note that cache misses will only occur in the +minimum of 100 tokens in it. The cache has a dual purpose:
LRU cache Note that cache misses will only occur in the multiple server case, where a user agent may have a signature guaranteed by a -secret token issued by another server in the cluster.
signature cache Since the cache always maintains a +secret token issued by another server in the cluster.
signature cache Since the cache always maintains a minimum of 100 (set by a parameter) tokens populated at startup, it can be used to provide a random token for signature purposes.
The per-thread cache functions as an L1 cache that indiscriminately caches -all secret tokens. Note that this is not an LRU cache +all secret tokens. Note that this is not an LRU cache because there is no cache eviction policy per se -- the cache is cleared when the thread is destroyed by AOLserver. -
Storing information on a client always presents an additional security risk.
Since we are only validating the information and not trying to protect it as a secret, we don't use salt. Cryptographic salt is useful if you are -trying to protect information from being read (e.g., hashing passwords).
External SSL mechanisms (firewall, dedicated hardware, etc.) can be used by creating two pools of AOLservers. In one pool the servers should be configured with the location parameter of nssock module set to -"https://yourservername". The servers in the other pool are +"https://yourservername". The servers in the other pool are configured as normal. The external SSL agent should direct SSL queries to the pool of secure servers, and it should direct non-SSL queries to the insecure servers. -
The pseudorandom number generator depends primarily on ns_rand, but is also seeded with ns_time and the number of page requests served since the server was started. The PRNG takes the SHA1(seed,ns_rand,ns_time,requests,clicks), and saves the first 40 bits as the seed for the next call to the PRNG in a thread-persistent global variable. The remaining 120 bits are rehashed to produce 160 bits of output. -
+ad_user_login user_id Logs the user in as user user_id. Optional forever flag determines whether or not permanent cookies are issued. -
ad_user_logout Logs the user out.
ad_check_password user_id password -returns 0 or 1.
ad_change_password user_id new -password
-ad_sign value Returns the digital signature of this +
ad_user_logout Logs the user out.
ad_check_password user_id password +returns 0 or 1.
ad_change_password user_id new +password
+ad_sign value Returns the digital signature of this value. Optional parameters allow for the specification of the secret used, the token_id used and the max_age for the signature. -ad_verify_signature value signatureReturns +ad_verify_signature value signatureReturns 1 or 0 indicating whether or not the signature matches the value specified. The secret parameter allows for specification of a different secret token to be used.
-ad_set_signed_cookie name data Sets a -signed cookie name with value data.
ad_get_signed_cookie name Gets the signed cookie +ad_set_signed_cookie name data Sets a +signed cookie name with value data.
ad_get_signed_cookie name Gets the signed cookie name. It raises an error if the cookie has been tampered with, or if -its expiration time has passed.
ad_set_client_property module name -data Sets a session property with name to value +its expiration time has passed.
ad_set_client_property module name +data Sets a session property with name to value data for the module module. The optional secure flag specifies the property should only be set if the client is authorized for -secure access (ad_secure_conn_p is true). There is also an optional -session_id flag to access data from sessions other than the current one.
ad_get_client_property module name
-data Gets a session property with name to for the
+secure access (ad_secure_conn_p
is true). There is also an optional
+session_id flag to access data from sessions other than the current one.
ad_get_client_property module name +data Gets a session property with name to for the module module. The optional secure flag specifies the property should only be retrieved if the client is authorized for secure access -(ad_secure_conn_p is true). There is also an optional -session_id flag to access data from sessions other than the current one.
-SessionTimeout the maximum time in seconds (default 1200) -between requests that are part of the same session
SessionRenew the time in seconds (default 300) between
+(ad_secure_conn_p
is true). There is also an optional
+session_id flag to access data from sessions other than the current one.
+SessionTimeout the maximum time in seconds (default 1200) +between requests that are part of the same session
SessionRenew the time in seconds (default 300) between reissue of the session cookie. The minimum time that can pass after a session cookie is issued and before it is rejected is (SessionTimeout - SessionRenew). This parameter is used so that only one session_id cookie is set on a single page even if there are multiple images that are being -downloaded.
SessionLifetime the maximum possible lifetime of a -session in seconds (default 604800 = 7 days)
NumberOfCachedSecretTokens the number of secret tokens to -cache. (default 100)
The pseudorandom number generator used in the OpenACS is cryptographically weak,
-and depends primarily on the randomness of the ns_rand function
+and depends primarily on the randomness of the ns_rand
function
for its randomness. The implementation of the PRNG could be substantially
improved.
-
Add a password argument. It is non-optimal to make the default behavior to assume that the password was provided. -
The secret tokens pool is currently static. Ideally, this pool should be changed on a random but regular basis, and the number of secret_tokens increased as the number of users come to the web site.
Since the security of the entire system depends on the secret tokens pool, access to the secret tokens table should be restricted and accessible via a strict PL/SQL API. This can be done by revoking standard SQL permissions on the table for the AOLserver user and giving those permissions to a PL/SQL -package.
Deferring session to creation until the second hit from a browser seems to be a good way of preventing a lot of overhead processing for robots. If we do this, send cookie on first hit to test if cookies are accepted, then actually allocate on second hit. To preserve a record of the first hit of the session, just include any info about that first hit in the probe cookie sent. Look at how usca_p (user session cookie attempted) is used in OpenACS 3.x ecommerce. -
Currently there are only session properties. Because sessions have a maximum life, properties have a maximum life. It would be nice to expand the interface to allow for more persistent properties. In the past, there was a @@ -303,7 +302,7 @@ can be shared between concurrent sessions). The applications should have control over the deletion patterns, but should not be able to ignore the amount of data stored. -
It would be nice to keep some info about sessions: first hit, last hit, and URLs visited come to mind. Both logging and API for accessing this info would be nice. WimpyPoint is an application that already wants to use this @@ -312,7 +311,7 @@ analyzers (leaving it in server memory for applications to access). Putting it into the database at all is probably too big a hammer. Certainly putting it into the database on every hit is too big a hammer. -
Two trends drive the requirement for removing cookie dependence. WAP browsers that do not have cookies, and publc perceptions of cookies as an invasion of privacy. The rely on the cookies mechanism in HTTP to distinguish one request from the next, and we trust it to force requests from the same @@ -331,21 +330,21 @@ Both of these problems can be mitigated by doing detection of cookie support (see the section on robot detection). To help deal with the first problem, One could also make the restriction that secure sessions are only allowed over -cookied HTTP.
This section is not meant to be a comprehensive analysis of the vulnerabilities of the security system. Listed below are possible attack points for the system; these vulnerabilities are currently theoretical in nature. The major cryptographic vulnerability of the system stems from the pseudorandom nature of the random number generators used in the system. -
Cryptographically weak PRNG see -above.
Dependence on sample -SQL command The list of random token that are placed in the secret +
Cryptographically weak PRNG see +above.
Dependence on sample
+SQL command The list of random token that are placed in the secret
tokens cache is randomly chosen by the Oracle
-sample command. This command may not be
+sample
command. This command may not be
entirely random, so predicting the contents of the secret tokens cache may not
-be as difficult as someone may anticipate.
Dependence on -ns_rand The actual token that is +be as difficult as someone may anticipate.
Dependence on
+ns_rand
The actual token that is
chosen from the cache to be used is chosen by a call to
-ns_rand.
ad_secure_conn_p
+ns_rand
.
ad_secure_conn_p
As discussed above, the security of the secure sessions authentication system is
dependent upon this function.