Hi,
After we recently experienced an XSS through what can only be described
as IE's shocking attempt at determining the mime type from the data and
ignoring what the server sent we decided to look into implementing
HTTP-only cookies. We know it's not a solution for preventing XSS, but
adding this would complicate the process for those wanting to exploit
any discovered problems before they are rectified.
HTTP-only is a feature in IE 6 SP1, Opera, Safari and KDE to allow the
setting of cookies that will only be sent via HTTP headers and never
accessible via client side scripting.
Ref: http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp
I’ve added the flags for setcookie and setrawcookie. There is also
support for the session system as well included.
The attached patches are for PHP 5.2 and HEAD.
Regards,
Scott
Index: ext/session/php_session.h
===================================================================
RCS file: /repository/php-src/ext/session/php_session.h,v
retrieving revision 1.101.2.2.2.1
diff -u -r1.101.2.2.2.1 php_session.h
--- ext/session/php_session.h 27 Jul 2006 15:33:16 -0000 1.101.2.2.2.1
+++ ext/session/php_session.h 7 Aug 2006 14:12:56 -0000
@@ -103,6 +103,7 @@
char *cookie_path;
char *cookie_domain;
zend_bool cookie_secure;
+ zend_bool cookie_httponly;
ps_module *mod;
void *mod_data;
php_session_status session_status;
Index: ext/session/session.c
===================================================================
RCS file: /repository/php-src/ext/session/session.c,v
retrieving revision 1.417.2.8.2.10
diff -u -r1.417.2.8.2.10 session.c
--- ext/session/session.c 2 Aug 2006 09:16:52 -0000 1.417.2.8.2.10
+++ ext/session/session.c 7 Aug 2006 13:46:01 -0000
@@ -165,6 +165,7 @@
STD_PHP_INI_ENTRY("session.cookie_path", "/",
PHP_INI_ALL, OnUpdateString, cookie_path, php_ps_globals, ps_globals)
STD_PHP_INI_ENTRY("session.cookie_domain", "",
PHP_INI_ALL, OnUpdateString, cookie_domain, php_ps_globals, ps_globals)
STD_PHP_INI_BOOLEAN("session.cookie_secure", "",
PHP_INI_ALL, OnUpdateBool, cookie_secure, php_ps_globals, ps_globals)
+ STD_PHP_INI_BOOLEAN("session.cookie_httponly", "",
PHP_INI_ALL, OnUpdateBool, cookie_httponly, php_ps_globals, ps_globals)
STD_PHP_INI_BOOLEAN("session.use_cookies", "1",
PHP_INI_ALL, OnUpdateBool, use_cookies, php_ps_globals, ps_globals)
STD_PHP_INI_BOOLEAN("session.use_only_cookies", "0",
PHP_INI_ALL, OnUpdateBool, use_only_cookies, php_ps_globals, ps_globals)
STD_PHP_INI_ENTRY("session.referer_check", "",
PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals, ps_globals)
@@ -1012,6 +1013,7 @@
#define COOKIE_PATH "; path="
#define COOKIE_DOMAIN "; domain="
#define COOKIE_SECURE "; secure"
+#define COOKIE_HTTPONLY "; HttpOnly"
static void php_session_send_cookie(TSRMLS_D)
{
@@ -1065,6 +1067,10 @@
smart_str_appends(&ncookie, COOKIE_SECURE);
}
+ if (PS(cookie_httponly)) {
+ smart_str_appends(&ncookie, COOKIE_HTTPONLY);
+ }
+
smart_str_0(&ncookie);
/* 'replace' must be 0 here, else a previous Set-Cookie
@@ -1296,13 +1302,13 @@
Set session cookie parameters */
PHP_FUNCTION(session_set_cookie_params)
{
- zval **lifetime, **path, **domain, **secure;
+ zval **lifetime, **path, **domain, **secure, **httponly;
if (!PS(use_cookies))
return;
- if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 4 ||
- zend_get_parameters_ex(ZEND_NUM_ARGS(), &lifetime, &path,
&domain, &secure) == FAILURE)
+ if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 5 ||
+ zend_get_parameters_ex(ZEND_NUM_ARGS(), &lifetime, &path,
&domain, &secure, &httponly) == FAILURE)
WRONG_PARAM_COUNT;
convert_to_string_ex(lifetime);
@@ -1319,6 +1325,10 @@
convert_to_long_ex(secure);
zend_alter_ini_entry("session.cookie_secure",
sizeof("session.cookie_secure"), Z_BVAL_PP(secure)?"1":"0", 1, PHP_INI_USER,
PHP_INI_STAGE_RUNTIME);
}
+ if (ZEND_NUM_ARGS() > 4) {
+ convert_to_long_ex(httponly);
+
zend_alter_ini_entry("session.cookie_httponly",
sizeof("session.cookie_httponly"), Z_BVAL_PP(httponly)?"1":"0", 1,
PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+ }
}
}
}
@@ -1338,6 +1348,7 @@
add_assoc_string(return_value, "path", PS(cookie_path), 1);
add_assoc_string(return_value, "domain", PS(cookie_domain), 1);
add_assoc_bool(return_value, "secure", PS(cookie_secure));
+ add_assoc_bool(return_value, "httponly", PS(cookie_httponly));
}
/* }}} */
Index: ext/standard/head.c
===================================================================
RCS file: /repository/php-src/ext/standard/head.c,v
retrieving revision 1.84.2.1
diff -u -r1.84.2.1 head.c
--- ext/standard/head.c 1 Jan 2006 12:50:14 -0000 1.84.2.1
+++ ext/standard/head.c 7 Aug 2006 13:48:51 -0000
@@ -60,7 +60,7 @@
}
-PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int url_encode TSRMLS_DC)
+PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int httponly, int url_encode TSRMLS_DC)
{
char *cookie, *encoded_value = NULL;
int len=sizeof("Set-Cookie: ");
@@ -131,6 +131,9 @@
if (secure) {
strcat(cookie, "; secure");
}
+ if (httponly) {
+ strcat(cookie, "; httponly");
+ }
ctr.line = cookie;
ctr.line_len = strlen(cookie);
@@ -142,22 +145,22 @@
/* php_set_cookie(name, value, expires, path, domain, secure) */
-/* {{{ proto bool setcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure]]]]])
+/* {{{ proto bool setcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure[, bool httponly]]]]]])
Send a cookie */
PHP_FUNCTION(setcookie)
{
char *name, *value = NULL, *path = NULL, *domain = NULL;
long expires = 0;
- zend_bool secure = 0;
+ zend_bool secure = 0, httponly = 0;
int name_len, value_len, path_len, domain_len;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssb", &name,
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssbb", &name,
&name_len, &value,
&value_len, &expires, &path,
- &path_len, &domain,
&domain_len, &secure) == FAILURE) {
+ &path_len, &domain,
&domain_len, &secure, &httponly) == FAILURE) {
return;
}
- if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, 1 TSRMLS_CC) == SUCCESS) {
+ if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, httponly, 1 TSRMLS_CC) == SUCCESS) {
RETVAL_TRUE;
} else {
RETVAL_FALSE;
@@ -165,22 +168,22 @@
}
/* }}} */
-/* {{{ proto bool setrawcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure]]]]])
+/* {{{ proto bool setrawcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure[, bool httponly]]]]]])
Send a cookie with no url encoding of the value */
PHP_FUNCTION(setrawcookie)
{
char *name, *value = NULL, *path = NULL, *domain = NULL;
long expires = 0;
- zend_bool secure = 0;
+ zend_bool secure = 0, httponly = 0;
int name_len, value_len, path_len, domain_len;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssb", &name,
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssbb", &name,
&name_len, &value,
&value_len, &expires, &path,
- &path_len, &domain,
&domain_len, &secure) == FAILURE) {
+ &path_len, &domain,
&domain_len, &secure, &httponly) == FAILURE) {
return;
}
- if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, 0 TSRMLS_CC) == SUCCESS) {
+ if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, httponly, 0 TSRMLS_CC) == SUCCESS) {
RETVAL_TRUE;
} else {
RETVAL_FALSE;
Index: ext/standard/head.h
===================================================================
RCS file: /repository/php-src/ext/standard/head.h,v
retrieving revision 1.28.2.1
diff -u -r1.28.2.1 head.h
--- ext/standard/head.h 1 Jan 2006 12:50:14 -0000 1.28.2.1
+++ ext/standard/head.h 7 Aug 2006 13:46:49 -0000
@@ -29,6 +29,6 @@
PHP_FUNCTION(headers_list);
PHPAPI int php_header(TSRMLS_D);
-PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int url_encode TSRMLS_DC);
+PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int httponly, int url_encode TSRMLS_DC);
#endif
Index: ext/session/php_session.h
===================================================================
RCS file: /repository/php-src/ext/session/php_session.h,v
retrieving revision 1.107
diff -u -r1.107 php_session.h
--- ext/session/php_session.h 27 Jul 2006 15:36:43 -0000 1.107
+++ ext/session/php_session.h 7 Aug 2006 14:06:45 -0000
@@ -103,6 +103,7 @@
char *cookie_path;
char *cookie_domain;
zend_bool cookie_secure;
+ zend_bool cookie_httponly;
ps_module *mod;
void *mod_data;
php_session_status session_status;
Index: ext/session/session.c
===================================================================
RCS file: /repository/php-src/ext/session/session.c,v
retrieving revision 1.445
diff -u -r1.445 session.c
--- ext/session/session.c 2 Aug 2006 09:15:13 -0000 1.445
+++ ext/session/session.c 7 Aug 2006 14:18:07 -0000
@@ -158,6 +158,7 @@
STD_PHP_INI_ENTRY("session.cookie_path", "/",
PHP_INI_ALL, OnUpdateString, cookie_path, php_ps_globals, ps_globals)
STD_PHP_INI_ENTRY("session.cookie_domain", "",
PHP_INI_ALL, OnUpdateString, cookie_domain, php_ps_globals, ps_globals)
STD_PHP_INI_BOOLEAN("session.cookie_secure", "",
PHP_INI_ALL, OnUpdateBool, cookie_secure, php_ps_globals, ps_globals)
+ STD_PHP_INI_BOOLEAN("session.cookie_httponly", "",
PHP_INI_ALL, OnUpdateBool, cookie_httponly, php_ps_globals, ps_globals)
STD_PHP_INI_BOOLEAN("session.use_cookies", "1",
PHP_INI_ALL, OnUpdateBool, use_cookies, php_ps_globals, ps_globals)
STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1",
PHP_INI_ALL, OnUpdateBool, use_only_cookies, php_ps_globals, ps_globals)
STD_PHP_INI_ENTRY("session.referer_check", "",
PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals, ps_globals)
@@ -902,6 +903,7 @@
#define COOKIE_PATH "; path="
#define COOKIE_DOMAIN "; domain="
#define COOKIE_SECURE "; secure"
+#define COOKIE_HTTPONLY "; HttpOnly"
static void php_session_send_cookie(TSRMLS_D)
{
@@ -955,6 +957,10 @@
smart_str_appends(&ncookie, COOKIE_SECURE);
}
+ if (PS(cookie_httponly)) {
+ smart_str_appends(&ncookie, COOKIE_HTTPONLY);
+ }
+
smart_str_0(&ncookie);
/* 'replace' must be 0 here, else a previous Set-Cookie
@@ -1186,13 +1192,13 @@
Set session cookie parameters */
PHP_FUNCTION(session_set_cookie_params)
{
- zval **lifetime, **path, **domain, **secure;
+ zval **lifetime, **path, **domain, **secure, **httponly;
if (!PS(use_cookies))
return;
- if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 4 ||
- zend_get_parameters_ex(ZEND_NUM_ARGS(), &lifetime, &path,
&domain, &secure) == FAILURE)
+ if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 5 ||
+ zend_get_parameters_ex(ZEND_NUM_ARGS(), &lifetime, &path,
&domain, &secure, &httponly) == FAILURE)
WRONG_PARAM_COUNT;
convert_to_string_ex(lifetime);
@@ -1209,6 +1215,10 @@
convert_to_long_ex(secure);
zend_alter_ini_entry("session.cookie_secure",
sizeof("session.cookie_secure"), Z_BVAL_PP(secure)?"1":"0", 1, PHP_INI_USER,
PHP_INI_STAGE_RUNTIME);
}
+ if (ZEND_NUM_ARGS() > 4) {
+ convert_to_long_ex(httponly);
+
zend_alter_ini_entry("session.cookie_httponly",
sizeof("session.cookie_httponly"), Z_BVAL_PP(httponly)?"1":"0", 1,
PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+ }
}
}
}
@@ -1228,6 +1238,7 @@
add_assoc_string(return_value, "path", PS(cookie_path), 1);
add_assoc_string(return_value, "domain", PS(cookie_domain), 1);
add_assoc_bool(return_value, "secure", PS(cookie_secure));
+ add_assoc_bool(return_value, "httponly", PS(cookie_httponly));
}
/* }}} */
Index: ext/standard/head.c
===================================================================
RCS file: /repository/php-src/ext/standard/head.c,v
retrieving revision 1.88
diff -u -r1.88 head.c
--- ext/standard/head.c 3 Jun 2006 11:19:43 -0000 1.88
+++ ext/standard/head.c 7 Aug 2006 14:12:24 -0000
@@ -59,7 +59,7 @@
}
-PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int url_encode TSRMLS_DC)
+PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int httponly, int url_encode TSRMLS_DC)
{
char *cookie, *encoded_value = NULL;
int len=sizeof("Set-Cookie: ");
@@ -130,6 +130,9 @@
if (secure) {
strcat(cookie, "; secure");
}
+ if (httponly) {
+ strcat(cookie, "; httponly");
+ }
ctr.line = cookie;
ctr.line_len = strlen(cookie);
@@ -141,22 +144,22 @@
/* php_set_cookie(name, value, expires, path, domain, secure) */
-/* {{{ proto bool setcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure]]]]])
+/* {{{ proto bool setcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure[, bool httponly]]]]]])
Send a cookie */
PHP_FUNCTION(setcookie)
{
char *name, *value = NULL, *path = NULL, *domain = NULL;
long expires = 0;
- zend_bool secure = 0;
+ zend_bool secure = 0, httponly = 0;
int name_len, value_len, path_len, domain_len;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssb", &name,
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssbb", &name,
&name_len, &value,
&value_len, &expires, &path,
- &path_len, &domain,
&domain_len, &secure) == FAILURE) {
+ &path_len, &domain,
&domain_len, &secure, &httponly) == FAILURE) {
return;
}
- if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, 1 TSRMLS_CC) == SUCCESS) {
+ if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, httponly, 1 TSRMLS_CC) == SUCCESS) {
RETVAL_TRUE;
} else {
RETVAL_FALSE;
@@ -164,22 +167,22 @@
}
/* }}} */
-/* {{{ proto bool setrawcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure]]]]])
+/* {{{ proto bool setrawcookie(string name [, string value [, int expires [,
string path [, string domain [, bool secure[, bool httponly]]]]]])
Send a cookie with no url encoding of the value */
PHP_FUNCTION(setrawcookie)
{
char *name, *value = NULL, *path = NULL, *domain = NULL;
long expires = 0;
- zend_bool secure = 0;
+ zend_bool secure = 0, httponly = 0;;
int name_len, value_len, path_len, domain_len;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssb", &name,
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssbb", &name,
&name_len, &value,
&value_len, &expires, &path,
- &path_len, &domain,
&domain_len, &secure) == FAILURE) {
+ &path_len, &domain,
&domain_len, &secure, &httponly) == FAILURE) {
return;
}
- if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, 0 TSRMLS_CC) == SUCCESS) {
+ if (php_setcookie(name, name_len, value, value_len, expires, path,
path_len, domain, domain_len, secure, httponly, 0 TSRMLS_CC) == SUCCESS) {
RETVAL_TRUE;
} else {
RETVAL_FALSE;
Index: ext/standard/head.h
===================================================================
RCS file: /repository/php-src/ext/standard/head.h,v
retrieving revision 1.29
diff -u -r1.29 head.h
--- ext/standard/head.h 1 Jan 2006 13:09:55 -0000 1.29
+++ ext/standard/head.h 7 Aug 2006 14:10:31 -0000
@@ -29,6 +29,6 @@
PHP_FUNCTION(headers_list);
PHPAPI int php_header(TSRMLS_D);
-PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int url_encode TSRMLS_DC);
+PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len,
time_t expires, char *path, int path_len, char *domain, int domain_len, int
secure, int httponly, int url_encode TSRMLS_DC);
#endif
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php