summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkkadosh2018-05-29 19:34:16 +0000
committerjvoisin2018-05-29 19:34:16 +0000
commit7832438b7abedf567ce6376f99949f419abcdff1 (patch)
tree560e43918d1dc36ce4cf760a5b27aed0c563bc1c
parent9eebe8c67e03e3041d454ea28e93996f7a67740b (diff)
Support session encryption
Implement session encryption.
-rw-r--r--doc/source/config.rst100
-rw-r--r--doc/source/encryption.rst132
-rw-r--r--doc/source/features.rst9
-rw-r--r--doc/source/index.rst1
-rw-r--r--src/config.m42
-rw-r--r--src/php_snuffleupagus.h2
-rw-r--r--src/snuffleupagus.c6
-rw-r--r--src/sp_config.c1
-rw-r--r--src/sp_config.h7
-rw-r--r--src/sp_config_keywords.c43
-rw-r--r--src/sp_config_keywords.h1
-rw-r--r--src/sp_cookie_encryption.c6
-rw-r--r--src/sp_crypt.c52
-rw-r--r--src/sp_crypt.h2
-rw-r--r--src/sp_session.c159
-rw-r--r--src/sp_session.h11
-rw-r--r--src/tests/config/config_crypt_session.ini2
-rw-r--r--src/tests/config/config_crypt_session_simul.ini3
-rw-r--r--src/tests/crypt_session_invalid.phpt24
-rw-r--r--src/tests/crypt_session_invalid_simul.phpt27
-rw-r--r--src/tests/crypt_session_read_uncrypt.phpt33
-rw-r--r--src/tests/crypt_session_valid.phpt27
-rw-r--r--src/tests/crypt_session_valid_simul.phpt27
-rw-r--r--src/tests/samesite_cookies.phpt51
24 files changed, 577 insertions, 151 deletions
diff --git a/doc/source/config.rst b/doc/source/config.rst
index d8389b6..b5bcad4 100644
--- a/doc/source/config.rst
+++ b/doc/source/config.rst
@@ -68,7 +68,7 @@ This configuration variable contains parameters that are used by multiple featur
68 sp.global.secret_key("44239bd400aa82e125337c9d4eb8315767411ccd"); 68 sp.global.secret_key("44239bd400aa82e125337c9d4eb8315767411ccd");
69 69
70- ``cookie_env_var``: A environment variable used as part of cookies encryption. 70- ``cookie_env_var``: A environment variable used as part of cookies encryption.
71 See the :ref:`relevant documentation <env-var-config>`. 71 See the :ref:`relevant documentation <cookie-encryption-config>`
72 72
73Bugclass-killer features 73Bugclass-killer features
74------------------------ 74------------------------
@@ -123,103 +123,9 @@ It can either be ``enabled`` or ``disabled`` and can be used in ``simulation`` m
123Cookies-related mitigations 123Cookies-related mitigations
124^^^^^^^^^^^^^^^^^^^^^^^^^^^ 124^^^^^^^^^^^^^^^^^^^^^^^^^^^
125 125
126.. warning:: 126Since snuffleupagus is providing several hardening features for cookies,
127 Those features are **not** available for session cookies `yet <https://github.com/nbs-system/snuffleupagus/issues/122>`_. 127there is a :dedicated web page:`here <cookie-encryption-config>` about them.
128
129auto_cookie_secure
130""""""""""""""""""
131
132:ref:`auto_cookie_secure <auto-cookie-secure-feature>`, disabled by default,
133will automatically mark cookies as `secure
134<https://en.wikipedia.org/wiki/HTTP_cookie#Secure_cookie>`_ when the web page
135is requested over HTTPS.
136
137It can either be ``enabled`` or ``disabled``.
138
139::
140
141 sp.auto_cookie_secure.enable();
142 sp.auto_cookie_secure.disable();
143
144cookie_samesite
145"""""""""""""""
146
147:ref:`samesite <samesite-feature>`, disabled by default, will add the `samesite
148<https://tools.ietf.org/html/draft-west-first-party-cookies-07>`_ attribute to
149cookies. It `prevents CSRF <https://www.owasp.org/index.php/SameSite>`_ but is
150not implemented by `all web browsers <https://caniuse.com/#search=samesite>`_
151yet.
152
153It can either be set to ``strict`` or ``lax``:
154
155- The ``lax`` attribute prevents cookies from being sent cross-domain for
156 "dangerous" methods, like ``POST``, ``PUT`` or ``DELETE``.
157
158- The ``strict`` one prevents any cookies from beind sent cross-domain.
159
160::
161
162 sp.cookie.name("cookie1").samesite("lax");
163 sp.cookie.name("cookie2").samesite("strict");;
164
165.. _cookie-encryption_config:
166
167cookie_encryption
168"""""""""""""""""
169
170.. warning::
171
172 To use this feature, you **must** set the :ref:`global.secret_key <config_global>`
173 and the :ref:`global.cookie_env_var <config_global>` variables.
174 This design decision prevents an attacker from
175 `trivially bruteforcing <https://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/>`_
176 or re-using session cookies.
177
178:ref:`cookie_secure <cookie-encryption-feature>`, disabled by default, will activate transparent encryption of specific cookies.
179
180It can either be ``enabled`` or ``disabled`` and can be used in ``simulation`` mode.
181
182::
183
184 sp.cookie.name("my_cookie_name").encrypt();
185 sp.cookie.name("another_cookie_name").encrypt();
186
187
188Removing the user-agent part
189............................
190
191Some web browser extensions, such as `uMatrix <https://github.com/gorhill/uMatrix/wiki>`__
192might be configured to change the user-agent on a regular basis. If you think that
193some of your users might be using configurations like this, you might want to disable
194the mixing of the user-agent in the cookie's encryption key. The simplest way to do
195so is to set the environment variable ``HTTP_USER_AGENT`` to a fixed value before passing
196it to your php process.
197
198We think that this use case is too exotic to be worth implementing as a
199proper configuration directive.
200
201.. _env-var-config:
202
203Choosing the proper environment variable
204........................................
205
206It's up to you to choose a meaningful environment variable to derive the key from.
207Suhosin `is using <https://www.suhosin.org/stories/configuration.html#suhosin-session-cryptraddr>`_
208the ``REMOTE_ADDR`` one, tying the validity of the cookie to the IP address of the user;
209unfortunately, nowadays, people are `roaming <https://en.wikipedia.org/wiki/Roaming>`_ a lot on their smartphone,
210hopping from WiFi to 4G.
211
212This is why we recommend, if possible, to use the *extended master secret*
213from TLS connections (`RFC7627 <https://tools.ietf.org/html/rfc7627>`_)
214instead. The will make the validity of the cookie TLS-dependent, by using the ``SSL_SESSION_ID`` variable.
215
216- In `Apache <https://httpd.apache.org/docs/current/mod/mod_ssl.html>`_,
217 it is possible to enable by adding ``SSLOptions StdEnvVars`` in your Apache2 configuration.
218- In `nginx <https://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables>`_,
219 you have to use ``fastcgi_param SSL_SESSION_ID $ssl_session_id if_not_empty;``.
220 128
221If you aren't using TLS (you should be), you can always use the ``REMOTE_ADDR`` one,
222or ``X-Real-IP`` if you're behind a reverse proxy.
223 129
224readonly_exec 130readonly_exec
225^^^^^^^^^^^^^ 131^^^^^^^^^^^^^
diff --git a/doc/source/encryption.rst b/doc/source/encryption.rst
new file mode 100644
index 0000000..8ac6861
--- /dev/null
+++ b/doc/source/encryption.rst
@@ -0,0 +1,132 @@
1.. _cookie-encryption-config:
2
3Cookies
4=======
5
6auto_cookie_secure
7""""""""""""""""""
8
9:ref:`auto_cookie_secure <auto-cookie-secure-feature>`, disabled by default,
10will automatically mark cookies as `secure
11<https://en.wikipedia.org/wiki/HTTP_cookie#Secure_cookie>`_ when the web page
12is requested over HTTPS.
13
14It can either be ``enabled`` or ``disabled``.
15
16::
17
18 sp.auto_cookie_secure.enable();
19 sp.auto_cookie_secure.disable();
20
21cookie_samesite
22"""""""""""""""
23
24:ref:`samesite <samesite-feature>`, disabled by default, will add the `samesite
25<https://tools.ietf.org/html/draft-west-first-party-cookies-07>`_ attribute to
26cookies. It `prevents CSRF <https://www.owasp.org/index.php/SameSite>`_ but is
27not implemented by `all web browsers <https://caniuse.com/#search=samesite>`_
28yet.
29
30It can either be set to ``strict`` or ``lax``:
31
32- The ``lax`` attribute prevents cookies from being sent cross-domain for
33 "dangerous" methods, like ``POST``, ``PUT`` or ``DELETE``.
34
35- The ``strict`` one prevents any cookies from beind sent cross-domain.
36
37::
38
39 sp.cookie.name("cookie1").samesite("lax");
40 sp.cookie.name("cookie2").samesite("strict");;
41
42.. _cookie-encryption_config:
43
44Cookie encryption
45"""""""""""""""""
46
47The encryption is done via the `tweetnacl library <https://tweetnacl.cr.yp.to/>`_,
48thus using `curve25519<https://en.wikipedia.org/wiki/Curve25519>`__, `xsalsa20 <https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant>`__ and `poly1305 <https://en.wikipedia.org/wiki/Poly1305>`__ for the encryption. We chose this
49library because of its portability, simplicity and reduced size (a single `.h` and
50`.c` file.).
51
52The key is derived from multiple sources, such as :
53 * The ``secret_key`` provided in the configuration.
54 * An environment variable, such as the ``REMOTE_ADDR`` (remote IP address) or the *extended master secret* from TLS connections (`RFC7627 <https://tools.ietf.org/html/rfc7627>`_)
55 * The user-agent.
56
57
58.. warning::
59
60 To use this feature, you **must** set the :ref:`global.secret_key <config_global>`
61 and the :ref:`global.cookie_env_var <config_global>` variables.
62 This design decision prevents an attacker from
63 `trivially bruteforcing <https://www.idontplaydarts.com/2011/11/decrypting-suhosin-sessions-and-cookies/>`_
64 or re-using session cookies.
65 If the simulation mode isn’t specified in the configuration, snuffleupagus will drop any request that it was unable to decrypt.
66
67Since PHP doesn't handle session cookie and non-session cookie in the same way,
68thus we are providing two different options:
69
70 * For the session cookie, the encryption happens server-side: The cookie's value isn't encrypted, only the session content is.
71 * For the non-session cookie, the value is encrypted.
72
73Session cookie
74..............
75
76:ref:`Session encryption <cookie-encryption-feature>`, disabled by default, will activate transparent session encryption.
77It can either be ``enabled`` or ``disabled`` and can be used in ``simulation`` mode.
78
79::
80
81 sp.session.encrypt();
82 sp.session.simulation();
83
84
85Non-session cookie
86..................
87
88:ref:`Cookie encryption <cookie-encryption-feature>`, disabled by default, will activate transparent encryption of specific cookies.
89
90It can either be ``enabled`` or ``disabled`` and can be used in ``simulation`` mode.
91
92::
93
94 sp.cookie.name("my_cookie_name").encrypt();
95 sp.cookie.name("another_cookie_name").encrypt();
96
97
98Removing the user-agent part
99............................
100
101Some web browser extensions, such as `uMatrix <https://github.com/gorhill/uMatrix/wiki>`__
102might be configured to change the user-agent on a regular basis. If you think that
103some of your users might be using configurations like this, you might want to disable
104the mixing of the user-agent in the cookie's encryption key. The simplest way to do
105so is to set the environment variable ``HTTP_USER_AGENT`` to a fixed value before passing
106it to your php process.
107
108We think that this use case is too exotic to be worth implementing as a
109proper configuration directive.
110
111.. _env-var-config:
112
113Choosing the proper environment variable
114........................................
115
116It's up to you to choose a meaningful environment variable to derive the key from.
117Suhosin `is using <https://www.suhosin.org/stories/configuration.html#suhosin-session-cryptraddr>`_
118the ``REMOTE_ADDR`` one, tying the validity of the cookie to the IP address of the user;
119unfortunately, nowadays, people are `roaming <https://en.wikipedia.org/wiki/Roaming>`_ a lot on their smartphone,
120hopping from WiFi to 4G.
121
122This is why we recommend, if possible, to use the *extended master secret*
123from TLS connections (`RFC7627 <https://tools.ietf.org/html/rfc7627>`_)
124instead. The will make the validity of the cookie TLS-dependent, by using the ``SSL_SESSION_ID`` variable.
125
126- In `Apache <https://httpd.apache.org/docs/current/mod/mod_ssl.html>`_,
127 it is possible to enable by adding ``SSLOptions StdEnvVars`` in your Apache2 configuration.
128- In `nginx <https://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables>`_,
129 you have to use ``fastcgi_param SSL_SESSION_ID $ssl_session_id if_not_empty;``.
130
131If you aren't using TLS (you should be), you can always use the ``REMOTE_ADDR`` one,
132or ``X-Real-IP`` if you're behind a reverse proxy.
diff --git a/doc/source/features.rst b/doc/source/features.rst
index 24c5074..08ad3d4 100644
--- a/doc/source/features.rst
+++ b/doc/source/features.rst
@@ -63,8 +63,8 @@ Examples of related vulnerabilities
63 63
64.. _cookie-encryption-feature: 64.. _cookie-encryption-feature:
65 65
66Session-cookie stealing via XSS 66Cookie stealing via XSS
67^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 67^^^^^^^^^^^^^^^^^^^^^^^
68 68
69The goto payload for XSS is often to steal cookies. 69The goto payload for XSS is often to steal cookies.
70Like *Suhosin*, we are encrypting the cookies with a secret key, 70Like *Suhosin*, we are encrypting the cookies with a secret key,
@@ -79,10 +79,7 @@ This feature is roughly the same than the `Suhosin one <https://suhosin.org/stor
79Having a secret server-side key will prevent anyone (even the user himself) 79Having a secret server-side key will prevent anyone (even the user himself)
80from reading the content of the cookie, reducing the impact of an application storing sensitive data client-side. 80from reading the content of the cookie, reducing the impact of an application storing sensitive data client-side.
81 81
82The encryption is done via the `tweetnacl library <https://tweetnacl.cr.yp.to/>`_, 82
83thus using curve25519, xsalsa20 and poly1305 for the encryption. We chose this
84library because of its portability, simplicity and reduced size (a single `.h` and
85`.c` file.).
86 83
87 84
88.. _fileupload-feature: 85.. _fileupload-feature:
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 28d0474..22cdcb8 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -19,6 +19,7 @@ Documentation
19 changelog 19 changelog
20 faq 20 faq
21 papers 21 papers
22 encryption
22 23
23Greetings 24Greetings
24--------- 25---------
diff --git a/src/config.m4 b/src/config.m4
index 9909da2..a4fea4d 100644
--- a/src/config.m4
+++ b/src/config.m4
@@ -6,7 +6,7 @@ sources="$sources sp_unserialize.c sp_utils.c sp_disable_xxe.c sp_list.c"
6sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c" 6sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c"
7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c" 7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c"
8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c" 8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c"
9sources="$sources sp_pcre_compat.c sp_crypt.c" 9sources="$sources sp_pcre_compat.c sp_crypt.c sp_session.c"
10 10
11PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support, 11PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support,
12[ --enable-snuffleupagus Enable snuffleupagus support]) 12[ --enable-snuffleupagus Enable snuffleupagus support])
diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h
index c658dac..f80ae66 100644
--- a/src/php_snuffleupagus.h
+++ b/src/php_snuffleupagus.h
@@ -18,6 +18,7 @@
18#include "ext/standard/info.h" 18#include "ext/standard/info.h"
19#include "ext/standard/php_var.h" 19#include "ext/standard/php_var.h"
20#include "ext/pcre/php_pcre.h" 20#include "ext/pcre/php_pcre.h"
21#include "ext/session/php_session.h"
21#include "php.h" 22#include "php.h"
22#include "php_ini.h" 23#include "php_ini.h"
23#include "zend_hash.h" 24#include "zend_hash.h"
@@ -41,6 +42,7 @@
41#include "sp_upload_validation.h" 42#include "sp_upload_validation.h"
42#include "sp_utils.h" 43#include "sp_utils.h"
43#include "sp_crypt.h" 44#include "sp_crypt.h"
45#include "sp_session.h"
44 46
45 47
46extern zend_module_entry snuffleupagus_module_entry; 48extern zend_module_entry snuffleupagus_module_entry;
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
index 3cdcfb9..c3fc686 100644
--- a/src/snuffleupagus.c
+++ b/src/snuffleupagus.c
@@ -81,6 +81,7 @@ PHP_GINIT_FUNCTION(snuffleupagus) {
81 SP_INIT(snuffleupagus_globals->config.config_disabled_functions); 81 SP_INIT(snuffleupagus_globals->config.config_disabled_functions);
82 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_ret); 82 SP_INIT(snuffleupagus_globals->config.config_disabled_functions_ret);
83 SP_INIT(snuffleupagus_globals->config.config_cookie); 83 SP_INIT(snuffleupagus_globals->config.config_cookie);
84 SP_INIT(snuffleupagus_globals->config.config_session);
84 SP_INIT(snuffleupagus_globals->config.config_disabled_constructs); 85 SP_INIT(snuffleupagus_globals->config.config_disabled_constructs);
85 SP_INIT(snuffleupagus_globals->config.config_eval); 86 SP_INIT(snuffleupagus_globals->config.config_eval);
86 87
@@ -124,6 +125,7 @@ PHP_MSHUTDOWN_FUNCTION(snuffleupagus) {
124 pefree(SNUFFLEUPAGUS_G(config.config_snuffleupagus), 1); 125 pefree(SNUFFLEUPAGUS_G(config.config_snuffleupagus), 1);
125 pefree(SNUFFLEUPAGUS_G(config.config_disable_xxe), 1); 126 pefree(SNUFFLEUPAGUS_G(config.config_disable_xxe), 1);
126 pefree(SNUFFLEUPAGUS_G(config.config_upload_validation), 1); 127 pefree(SNUFFLEUPAGUS_G(config.config_upload_validation), 1);
128 pefree(SNUFFLEUPAGUS_G(config.config_session), 1);
127 129
128#define FREE_LST_DISABLE(L) \ 130#define FREE_LST_DISABLE(L) \
129 do { \ 131 do { \
@@ -229,6 +231,10 @@ static PHP_INI_MH(OnUpdateConfiguration) {
229 } 231 }
230 hook_cookies(); 232 hook_cookies();
231 233
234 if (SNUFFLEUPAGUS_G(config).config_session->encrypt) {
235 hook_session();
236 }
237
232 if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) { 238 if (true == SNUFFLEUPAGUS_G(config).config_global_strict->enable) {
233 if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) { 239 if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) {
234 zend_extension_entry.startup = NULL; 240 zend_extension_entry.startup = NULL;
diff --git a/src/sp_config.c b/src/sp_config.c
index 67140a0..a89174a 100644
--- a/src/sp_config.c
+++ b/src/sp_config.c
@@ -21,6 +21,7 @@ sp_config_tokens const sp_func[] = {
21 {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE}, 21 {.func = parse_disable_xxe, .token = SP_TOKEN_DISABLE_XXE},
22 {.func = parse_eval_blacklist, .token = SP_TOKEN_EVAL_BLACKLIST}, 22 {.func = parse_eval_blacklist, .token = SP_TOKEN_EVAL_BLACKLIST},
23 {.func = parse_eval_whitelist, .token = SP_TOKEN_EVAL_WHITELIST}, 23 {.func = parse_eval_whitelist, .token = SP_TOKEN_EVAL_WHITELIST},
24 {.func = parse_session, .token = SP_TOKEN_SESSION_ENCRYPTION},
24 {NULL, NULL}}; 25 {NULL, NULL}};
25 26
26/* Top level keyword parsing */ 27/* Top level keyword parsing */
diff --git a/src/sp_config.h b/src/sp_config.h
index e537ec2..b44960f 100644
--- a/src/sp_config.h
+++ b/src/sp_config.h
@@ -66,6 +66,11 @@ typedef struct {
66} sp_cookie; 66} sp_cookie;
67 67
68typedef struct { 68typedef struct {
69 bool encrypt;
70 bool simulation;
71} sp_config_session;
72
73typedef struct {
69 bool enable; 74 bool enable;
70 bool simulation; 75 bool simulation;
71 char *dump; 76 char *dump;
@@ -158,6 +163,7 @@ typedef struct {
158 sp_config_disable_xxe *config_disable_xxe; 163 sp_config_disable_xxe *config_disable_xxe;
159 sp_config_disabled_constructs *config_disabled_constructs; 164 sp_config_disabled_constructs *config_disabled_constructs;
160 sp_config_eval *config_eval; 165 sp_config_eval *config_eval;
166 sp_config_session *config_session;
161} sp_config; 167} sp_config;
162 168
163typedef struct { 169typedef struct {
@@ -175,6 +181,7 @@ typedef struct {
175 181
176#define SP_TOKEN_AUTO_COOKIE_SECURE ".auto_cookie_secure" 182#define SP_TOKEN_AUTO_COOKIE_SECURE ".auto_cookie_secure"
177#define SP_TOKEN_COOKIE_ENCRYPTION ".cookie" 183#define SP_TOKEN_COOKIE_ENCRYPTION ".cookie"
184#define SP_TOKEN_SESSION_ENCRYPTION ".session"
178#define SP_TOKEN_DISABLE_FUNC ".disable_function" 185#define SP_TOKEN_DISABLE_FUNC ".disable_function"
179#define SP_TOKEN_GLOBAL ".global" 186#define SP_TOKEN_GLOBAL ".global"
180#define SP_TOKEN_GLOBAL_STRICT ".global_strict" 187#define SP_TOKEN_GLOBAL_STRICT ".global_strict"
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index 9faaafb..f702f4d 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -60,6 +60,49 @@ static int parse_enable(char *line, bool *restrict retval,
60 return ret; 60 return ret;
61} 61}
62 62
63int parse_session(char *line) {
64 sp_config_session *session =
65 pecalloc(sizeof(sp_config_session), 1, 0);
66
67 sp_config_functions sp_config_funcs_session_encryption[] = {
68 {parse_empty, SP_TOKEN_ENCRYPT, &(session->encrypt)},
69 {parse_empty, SP_TOKEN_SIMULATION, &(session->simulation)},
70 {0}};
71 int ret = parse_keywords(sp_config_funcs_session_encryption, line);
72 if (0 != ret) {
73 return ret;
74 }
75 if (session->encrypt) {
76 if (0 == (SNUFFLEUPAGUS_G(config).config_snuffleupagus->cookies_env_var)) {
77 sp_log_err(
78 "config",
79 "You're trying to use the session cookie encryption feature"
80 "on line %zu without having set the `.cookie_env_var` option in"
81 "`sp.global`: please set it first.",
82 sp_line_no);
83 pefree(session, 0);
84 return -1;
85 } else if (0 ==
86 (SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key)) {
87 sp_log_err(
88 "config",
89 "You're trying to use the session cookie encryption feature"
90 "on line %zu without having set the `.encryption_key` option in"
91 "`sp.global`: please set it first.",
92 sp_line_no);
93 pefree(session, 0);
94 return -1;
95 }
96 }
97
98 SNUFFLEUPAGUS_G(config).config_session->encrypt =
99 session->encrypt;
100 SNUFFLEUPAGUS_G(config).config_session->simulation =
101 session->simulation;
102 pefree(session, 0);
103 return ret;
104}
105
63int parse_random(char *line) { 106int parse_random(char *line) {
64 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_random->enable), 107 return parse_enable(line, &(SNUFFLEUPAGUS_G(config).config_random->enable),
65 NULL); 108 NULL);
diff --git a/src/sp_config_keywords.h b/src/sp_config_keywords.h
index 4205dac..f1f414a 100644
--- a/src/sp_config_keywords.h
+++ b/src/sp_config_keywords.h
@@ -14,5 +14,6 @@ int parse_disabled_functions(char *line);
14int parse_upload_validation(char *line); 14int parse_upload_validation(char *line);
15int parse_eval_blacklist(char *line); 15int parse_eval_blacklist(char *line);
16int parse_eval_whitelist(char *line); 16int parse_eval_whitelist(char *line);
17int parse_session(char *line);
17 18
18#endif // __SP_CONFIG_KEYWORDS_H 19#endif // __SP_CONFIG_KEYWORDS_H
diff --git a/src/sp_cookie_encryption.c b/src/sp_cookie_encryption.c
index 9030112..72223ad 100644
--- a/src/sp_cookie_encryption.c
+++ b/src/sp_cookie_encryption.c
@@ -21,7 +21,6 @@ static inline const sp_cookie *sp_lookup_cookie_config(const char *key) {
21int decrypt_cookie(zval *pDest, int num_args, va_list args, 21int decrypt_cookie(zval *pDest, int num_args, va_list args,
22 zend_hash_key *hash_key) { 22 zend_hash_key *hash_key) {
23 const sp_cookie *cookie = sp_lookup_cookie_config(ZSTR_VAL(hash_key->key)); 23 const sp_cookie *cookie = sp_lookup_cookie_config(ZSTR_VAL(hash_key->key));
24 int ret = 0;
25 24
26 /* If the cookie isn't in the conf, it shouldn't be encrypted. */ 25 /* If the cookie isn't in the conf, it shouldn't be encrypted. */
27 if (!cookie || !cookie->encrypt) { 26 if (!cookie || !cookie->encrypt) {
@@ -36,11 +35,6 @@ int decrypt_cookie(zval *pDest, int num_args, va_list args,
36 return decrypt_zval(pDest, cookie->simulation, hash_key); 35 return decrypt_zval(pDest, cookie->simulation, hash_key);
37} 36}
38 37
39/*
40** This function will return the `data` of length `data_len` encrypted in the
41** form `base64(nonce | encrypted_data)` (with `|` being the concatenation
42** operation).
43*/
44static zend_string *encrypt_data(char *data, unsigned long long data_len) { 38static zend_string *encrypt_data(char *data, unsigned long long data_len) {
45 zend_string *z = encrypt_zval(data, data_len); 39 zend_string *z = encrypt_zval(data, data_len);
46 sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val); 40 sp_log_debug("cookie_encryption", "Cookie value:%s:", z->val);
diff --git a/src/sp_crypt.c b/src/sp_crypt.c
index 0c40f1f..55ae37b 100644
--- a/src/sp_crypt.c
+++ b/src/sp_crypt.c
@@ -4,9 +4,7 @@
4 4
5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) 5ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
6 6
7static zend_long nonce_d = 0; 7void generate_key(unsigned char *key) {
8
9static void generate_key(unsigned char *key) {
10 PHP_SHA256_CTX ctx; 8 PHP_SHA256_CTX ctx;
11 const char *user_agent = getenv("HTTP_USER_AGENT"); 9 const char *user_agent = getenv("HTTP_USER_AGENT");
12 const char *env_var = 10 const char *env_var =
@@ -50,14 +48,13 @@ int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) {
50 debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), 48 debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)),
51 Z_STRLEN_P(pDest)); 49 Z_STRLEN_P(pDest));
52 50
53 if (ZSTR_LEN(debase64) < 51 if (ZSTR_LEN(debase64) < crypto_secretbox_NONCEBYTES) {
54 crypto_secretbox_NONCEBYTES + crypto_secretbox_ZEROBYTES) {
55 if (true == simulation) { 52 if (true == simulation) {
56 sp_log_msg( 53 sp_log_msg(
57 "cookie_encryption", SP_LOG_SIMULATION, 54 "cookie_encryption", SP_LOG_SIMULATION,
58 "Buffer underflow tentative detected in cookie encryption handling " 55 "Buffer underflow tentative detected in cookie encryption handling "
59 "for %s. Using the cookie 'as it' instead of decrypting it.", 56 "for %s. Using the cookie 'as it' instead of decrypting it.",
60 ZSTR_VAL(hash_key->key)); 57 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
61 return ZEND_HASH_APPLY_KEEP; 58 return ZEND_HASH_APPLY_KEEP;
62 } else { 59 } else {
63 sp_log_msg( 60 sp_log_msg(
@@ -67,9 +64,26 @@ int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) {
67 } 64 }
68 } 65 }
69 66
67
68 if (ZSTR_LEN(debase64) + (size_t)crypto_secretbox_ZEROBYTES < ZSTR_LEN(debase64)) {
69 if (true == simulation) {
70 sp_log_msg(
71 "cookie_encryption", SP_LOG_SIMULATION,
72 "Integer overflow tentative detected in cookie encryption handling "
73 "for %s. Using the cookie 'as it' instead of decrypting it.",
74 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
75 return ZEND_HASH_APPLY_KEEP;
76 } else {
77 sp_log_msg(
78 "cookie_encryption", SP_LOG_DROP,
79 "Integer overflow tentative detected in cookie encryption handling.");
80 return ZEND_HASH_APPLY_REMOVE;
81 }
82 }
83
70 generate_key(key); 84 generate_key(key);
71 85
72 decrypted = ecalloc(ZSTR_LEN(debase64), 1); 86 decrypted = ecalloc(ZSTR_LEN(debase64) + crypto_secretbox_ZEROBYTES, 1);
73 87
74 ret = crypto_secretbox_open( 88 ret = crypto_secretbox_open(
75 decrypted, 89 decrypted,
@@ -83,12 +97,12 @@ int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) {
83 "cookie_encryption", SP_LOG_SIMULATION, 97 "cookie_encryption", SP_LOG_SIMULATION,
84 "Something went wrong with the decryption of %s. Using the cookie " 98 "Something went wrong with the decryption of %s. Using the cookie "
85 "'as it' instead of decrypting it", 99 "'as it' instead of decrypting it",
86 ZSTR_VAL(hash_key->key)); 100 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
87 return ZEND_HASH_APPLY_KEEP; 101 return ZEND_HASH_APPLY_KEEP;
88 } else { 102 } else {
89 sp_log_msg("cookie_encryption", SP_LOG_DROP, 103 sp_log_msg("cookie_encryption", SP_LOG_DROP,
90 "Something went wrong with the decryption of %s.", 104 "Something went wrong with the decryption of %s.",
91 ZSTR_VAL(hash_key->key)); 105 hash_key ? ZSTR_VAL(hash_key->key) : "the session");
92 return ZEND_HASH_APPLY_REMOVE; 106 return ZEND_HASH_APPLY_REMOVE;
93 } 107 }
94 } 108 }
@@ -100,8 +114,14 @@ int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) {
100 return ZEND_HASH_APPLY_KEEP; 114 return ZEND_HASH_APPLY_KEEP;
101} 115}
102 116
117/*
118** This function will return the `data` of length `data_len` encrypted in the
119** form `base64(nonce | encrypted_data)` (with `|` being the concatenation
120** operation).
121*/
103zend_string *encrypt_zval(char *data, unsigned long long data_len) { 122zend_string *encrypt_zval(char *data, unsigned long long data_len) {
104 const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1; 123 const size_t encrypted_msg_len = crypto_secretbox_ZEROBYTES + data_len + 1;
124 // FIXME : We know that this len is too long
105 const size_t emsg_and_nonce_len = 125 const size_t emsg_and_nonce_len =
106 encrypted_msg_len + crypto_secretbox_NONCEBYTES; 126 encrypted_msg_len + crypto_secretbox_NONCEBYTES;
107 127
@@ -112,25 +132,21 @@ zend_string *encrypt_zval(char *data, unsigned long long data_len) {
112 132
113 generate_key(key); 133 generate_key(key);
114 134
135 // Put random bytes in the nonce
136 php_random_bytes(nonce, sizeof(nonce), 0);
137
115 /* tweetnacl's API requires the message to be padded with 138 /* tweetnacl's API requires the message to be padded with
116 crypto_secretbox_ZEROBYTES zeroes. */ 139 crypto_secretbox_ZEROBYTES zeroes. */
117 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len); 140 memcpy(data_to_encrypt + crypto_secretbox_ZEROBYTES, data, data_len);
118 141
119 assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES); 142 assert(sizeof(zend_long) <= crypto_secretbox_NONCEBYTES);
120 143
121 if (0 == nonce_d) {
122 /* A zend_long should be enough to avoid collisions */
123 if (php_random_int_throw(0, ZEND_LONG_MAX, &nonce_d) == FAILURE) {
124 return NULL; // LCOV_EXCL_LINE
125 }
126 }
127 nonce_d++;
128 sscanf((char *)nonce, "%ld", &nonce_d);
129
130 memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES); 144 memcpy(encrypted_data, nonce, crypto_secretbox_NONCEBYTES);
145
131 crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES, 146 crypto_secretbox(encrypted_data + crypto_secretbox_NONCEBYTES,
132 data_to_encrypt, encrypted_msg_len, nonce, key); 147 data_to_encrypt, encrypted_msg_len, nonce, key);
133 148
134 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len); 149 zend_string *z = php_base64_encode(encrypted_data, emsg_and_nonce_len);
150
135 return z; 151 return z;
136} \ No newline at end of file 152} \ No newline at end of file
diff --git a/src/sp_crypt.h b/src/sp_crypt.h
index 1852a0a..3ede104 100644
--- a/src/sp_crypt.h
+++ b/src/sp_crypt.h
@@ -10,7 +10,7 @@
10#include "ext/hash/php_hash_sha.h" 10#include "ext/hash/php_hash_sha.h"
11#include "ext/standard/base64.h" 11#include "ext/standard/base64.h"
12 12
13static void generate_key(unsigned char *key); 13void generate_key(unsigned char *key);
14int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hask_key); 14int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hask_key);
15zend_string *encrypt_zval(char *data, unsigned long long data_len); 15zend_string *encrypt_zval(char *data, unsigned long long data_len);
16 16
diff --git a/src/sp_session.c b/src/sp_session.c
new file mode 100644
index 0000000..4085007
--- /dev/null
+++ b/src/sp_session.c
@@ -0,0 +1,159 @@
1#include "php_snuffleupagus.h"
2#include "ext/session/php_session.h"
3
4ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus);
5
6#ifdef ZTS
7static ts_rsrc_id session_globals_id = 0;
8#define SESSION_G(v) ZEND_TSRMG(session_globals_id, php_ps_globals *, v)
9#ifdef COMPILE_DL_SESSION
10ZEND_TSRMLS_CACHE_EXTERN();
11#endif
12#else
13#define SESSION_G(v) (ps_globals.v)
14#endif
15
16static php_ps_globals *session_globals = NULL;
17static ps_module *s_module;
18static ps_module *s_original_mod;
19static int (*old_s_read)(PS_READ_ARGS);
20static int (*old_s_write)(PS_WRITE_ARGS);
21static int (*previous_sessionRINIT)(INIT_FUNC_ARGS) = NULL;
22static ZEND_INI_MH((*old_OnUpdateSaveHandler)) = NULL;
23
24
25static int sp_hook_s_read(PS_READ_ARGS) {
26 int r = old_s_read(mod_data, key, val, maxlifetime);
27 if (r == SUCCESS && SNUFFLEUPAGUS_G(config).config_session->encrypt &&
28 val != NULL && *val != NULL && ZSTR_LEN(*val)) {
29 zend_string *orig_val = *val;
30 zval val_zval;
31 ZVAL_PSTRINGL(&val_zval, ZSTR_VAL(*val), ZSTR_LEN(*val));
32
33 int ret = decrypt_zval(
34 &val_zval, SNUFFLEUPAGUS_G(config).config_session->simulation,
35 NULL);
36 if (0 != ret) {
37 if (SNUFFLEUPAGUS_G(config).config_session->simulation) {
38 return ret;
39 } else {
40 sp_terminate();
41 }
42 }
43
44 *val = zend_string_dup(val_zval.value.str, 0);
45 if (*val == NULL) {
46 *val = ZSTR_EMPTY_ALLOC();
47 }
48 zend_string_release(orig_val);
49 }
50
51 return r;
52}
53
54
55static int sp_hook_s_write(PS_WRITE_ARGS) {
56 if (ZSTR_LEN(val) > 0 &&
57 SNUFFLEUPAGUS_G(config).config_session->encrypt) {
58 zend_string *new_val = encrypt_zval(ZSTR_VAL(val), ZSTR_LEN(val));
59 return old_s_write(mod_data, key, new_val, maxlifetime);
60 }
61 return old_s_write(mod_data, key, val, maxlifetime);
62}
63
64static void sp_hook_session_module() {
65 ps_module *old_mod = SESSION_G(mod);
66 ps_module *mod;
67
68 if (old_mod == NULL || s_module == old_mod) {
69 return;
70 }
71
72 if (s_module == NULL) {
73 s_module = mod = malloc(sizeof(ps_module));
74 if (mod == NULL) {
75 return;
76 }
77 }
78
79 s_original_mod = old_mod;
80
81 mod = s_module;
82 memcpy(mod, old_mod, sizeof(ps_module));
83
84 old_s_read = mod->s_read;
85 mod->s_read = sp_hook_s_read;
86
87 old_s_write = mod->s_write;
88 mod->s_write = sp_hook_s_write;
89
90 SESSION_G(mod) = mod;
91}
92
93static PHP_INI_MH(sp_OnUpdateSaveHandler) {
94 if (stage == PHP_INI_STAGE_RUNTIME &&
95 SESSION_G(session_status) == php_session_none &&
96 s_original_mod &&
97 zend_string_equals_literal(new_value, "user") == 0 &&
98 strcmp(((ps_module *)s_original_mod)->s_name, "user") ==
99 0) {
100 return SUCCESS;
101 }
102
103 SESSION_G(mod) = s_original_mod;
104
105 int r = old_OnUpdateSaveHandler(entry, new_value, mh_arg1, mh_arg2, mh_arg3,
106 stage);
107
108 sp_hook_session_module();
109
110 return r;
111}
112
113static int sp_hook_session_RINIT(INIT_FUNC_ARGS) {
114 if (SESSION_G(mod) == NULL) {
115 zend_ini_entry *ini_entry;
116 if ((ini_entry = zend_hash_str_find_ptr(
117 EG(ini_directives), ZEND_STRL("session.save_handler")))) {
118 if (ini_entry->value) {
119 sp_OnUpdateSaveHandler(NULL, ini_entry->value, NULL, NULL, NULL, 0);
120 }
121 }
122 }
123 return previous_sessionRINIT(INIT_FUNC_ARGS_PASSTHRU);
124}
125
126void hook_session() {
127 zend_module_entry *module;
128
129 if ((module = zend_hash_str_find_ptr(&module_registry,
130 ZEND_STRL("session"))) == NULL) {
131 return;
132 }
133
134#ifdef ZTS
135 if (session_globals_id == 0) {
136 session_globals_id = *module->globals_id_ptr;
137 }
138#else
139 if (session_globals == NULL) {
140 session_globals = module->globals_ptr;
141 }
142#endif
143 if (old_OnUpdateSaveHandler != NULL) {
144 return;
145 }
146
147 previous_sessionRINIT = module->request_startup_func;
148 module->request_startup_func = sp_hook_session_RINIT;
149
150 zend_ini_entry *ini_entry;
151 if ((ini_entry = zend_hash_str_find_ptr(
152 EG(ini_directives), ZEND_STRL("session.save_handler"))) != NULL) {
153 old_OnUpdateSaveHandler = ini_entry->on_modify;
154 ini_entry->on_modify = sp_OnUpdateSaveHandler;
155 }
156 s_module = NULL;
157
158 sp_hook_session_module();
159} \ No newline at end of file
diff --git a/src/sp_session.h b/src/sp_session.h
new file mode 100644
index 0000000..c2a0357
--- /dev/null
+++ b/src/sp_session.h
@@ -0,0 +1,11 @@
1#include "SAPI.h"
2#include "tweetnacl.h"
3
4#include "sp_utils.h"
5
6#include "ext/hash/php_hash.h"
7#include "ext/hash/php_hash_sha.h"
8#include "ext/standard/base64.h"
9
10
11void hook_session();
diff --git a/src/tests/config/config_crypt_session.ini b/src/tests/config/config_crypt_session.ini
new file mode 100644
index 0000000..14b0c2c
--- /dev/null
+++ b/src/tests/config/config_crypt_session.ini
@@ -0,0 +1,2 @@
1sp.global.secret_key("abcdef").cookie_env_var("REMOTE_ADDR");
2sp.session.encrypt(); \ No newline at end of file
diff --git a/src/tests/config/config_crypt_session_simul.ini b/src/tests/config/config_crypt_session_simul.ini
new file mode 100644
index 0000000..fbd43eb
--- /dev/null
+++ b/src/tests/config/config_crypt_session_simul.ini
@@ -0,0 +1,3 @@
1sp.global.secret_key("abcdef").cookie_env_var("REMOTE_ADDR");
2sp.session.encrypt();
3sp.session.simulation(); \ No newline at end of file
diff --git a/src/tests/crypt_session_invalid.phpt b/src/tests/crypt_session_invalid.phpt
new file mode 100644
index 0000000..687a407
--- /dev/null
+++ b/src/tests/crypt_session_invalid.phpt
@@ -0,0 +1,24 @@
1--TEST--
2SESSION crypt and bad decrypt
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_crypt_session.ini
7--ENV--
8return <<<EOF
9REMOTE_ADDR=127.0.0.1
10EOF;
11--FILE--
12<?php
13// Do it like that to write (encrypt) the session and then to read (decrypt) the session
14session_start(); // Start new_session , it will read an empty session
15$_SESSION["toto"] = "tata"; // Encrypt and write the session
16$id = session_id(); // Get the session_id to use it later
17session_write_close(); // Close the session
18putenv("REMOTE_ADDR=127.0.0.2");
19session_id($id); // Recover the session with the previous session_id
20session_start(); // Re start the session, It will read and decrypt the non empty session
21var_dump($_SESSION); // Dump the session
22?>
23--EXPECTF--
24[snuffleupagus][127.0.0.2][cookie_encryption][drop] Something went wrong with the decryption of the session. \ No newline at end of file
diff --git a/src/tests/crypt_session_invalid_simul.phpt b/src/tests/crypt_session_invalid_simul.phpt
new file mode 100644
index 0000000..7bfefcb
--- /dev/null
+++ b/src/tests/crypt_session_invalid_simul.phpt
@@ -0,0 +1,27 @@
1--TEST--
2SESSION crypt and bad decrypt
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_crypt_session_simul.ini
7--ENV--
8return <<<EOF
9REMOTE_ADDR=127.0.0.1
10EOF;
11--FILE--
12<?php
13// Do it like that to write (encrypt) the session and then to read (decrypt) the session
14session_start(); // Start new_session , it will read an empty session
15$_SESSION["toto"] = "tata"; // Encrypt and write the session
16$id = session_id(); // Get the session_id to use it later
17session_write_close(); // Close the session
18putenv("REMOTE_ADDR=127.0.0.2");
19session_id($id); // Recover the session with the previous session_id
20session_start(); // Re start the session, It will read and decrypt the non empty session
21var_dump($_SESSION); // Dump the session
22?>
23--EXPECTF--
24array(1) {
25 ["toto"]=>
26 string(4) "tata"
27}
diff --git a/src/tests/crypt_session_read_uncrypt.phpt b/src/tests/crypt_session_read_uncrypt.phpt
new file mode 100644
index 0000000..f15d8b6
--- /dev/null
+++ b/src/tests/crypt_session_read_uncrypt.phpt
@@ -0,0 +1,33 @@
1--TEST--
2SESSION crypt/decrypt valid
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_crypt_session_simul.ini
7--ENV--
8return <<<EOF
9REMOTE_ADDR=127.0.0.1
10EOF;
11--FILE--
12<?php
13$current_path = dirname(getcwd()) . "/src/tests/" ;
14ini_set("session.save_path", $current_path);
15
16session_start();
17$id = session_id(); // Get the session_id to use it later
18$filename_sess = $current_path . "sess_" . $id;
19file_put_contents($filename_sess, "toto|s:4:\"tata\";"); // Write a unencrypted session
20session_write_close(); // Close the session
21
22session_id($id);
23session_start(); // Try to read the unencrypted session, it will fail to decrypt but it must return the session
24var_dump($_SESSION);
25echo "OK";
26unlink($filename_sess);
27?>
28--EXPECTF--
29array(1) {
30 ["toto"]=>
31 string(4) "tata"
32}
33OK
diff --git a/src/tests/crypt_session_valid.phpt b/src/tests/crypt_session_valid.phpt
new file mode 100644
index 0000000..bf9fea0
--- /dev/null
+++ b/src/tests/crypt_session_valid.phpt
@@ -0,0 +1,27 @@
1--TEST--
2SESSION crypt/decrypt valid
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_crypt_session.ini
7--ENV--
8return <<<EOF
9REMOTE_ADDR=127.0.0.1
10EOF;
11--FILE--
12<?php
13// Do it like that to write (encrypt) the session and then to read (decrypt) the session
14session_start(); // Start new_session , it will read an empty session
15$_SESSION["toto"] = "tata"; // Encrypt and write the session
16$id = session_id(); // Get the session_id to use it later
17
18session_write_close(); // Close the session
19session_id($id); // Recover the session with the previous session_id
20session_start(); // Re start the session, It will read and decrypt the non empty session
21var_dump($_SESSION); // Dump the session
22?>
23--EXPECTF--
24array(1) {
25 ["toto"]=>
26 string(4) "tata"
27}
diff --git a/src/tests/crypt_session_valid_simul.phpt b/src/tests/crypt_session_valid_simul.phpt
new file mode 100644
index 0000000..28083cf
--- /dev/null
+++ b/src/tests/crypt_session_valid_simul.phpt
@@ -0,0 +1,27 @@
1--TEST--
2SESSION crypt/decrypt valid
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) die "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_crypt_session_simul.ini
7--ENV--
8return <<<EOF
9REMOTE_ADDR=127.0.0.1
10EOF;
11--FILE--
12<?php
13// Do it like that to write (encrypt) the session and then to read (decrypt) the session
14session_start(); // Start new_session , it will read an empty session
15$_SESSION["toto"] = "tata"; // Encrypt and write the session
16$id = session_id(); // Get the session_id to use it later
17session_write_close(); // Close the session
18
19session_id($id); // Recover the session with the previous session_id
20session_start(); // Re start the session, It will read and decrypt the non empty session
21var_dump($_SESSION); // Dump the session
22?>
23--EXPECTF--
24array(1) {
25 ["toto"]=>
26 string(4) "tata"
27}
diff --git a/src/tests/samesite_cookies.phpt b/src/tests/samesite_cookies.phpt
index fe74172..d010963 100644
--- a/src/tests/samesite_cookies.phpt
+++ b/src/tests/samesite_cookies.phpt
@@ -27,12 +27,13 @@ if (!setcookie("nice_cookie", "nice_value", 1, "1", "1", true, true)) {
27 echo "setcookie failed.\n"; 27 echo "setcookie failed.\n";
28} 28}
29 29
30// If the cookie value start with "!", it means that we don't want the value in the headers, but the encrypted cookie
30$expected = array( 31$expected = array(
31 'Set-Cookie: super_cookie=super_value; path=; samesite=Lax', 32 "awful_cookie" => "!awful_value",
32 'Set-Cookie: awful_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFyZcYjfEskB0AU0V3%2BvwazcRuU%2Ft6KpcUahvxw%3D; path=; samesite=Strict; HttpOnly', 33 "not_encrypted" => "test_value",
33 'Set-Cookie: not_encrypted=test_value; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=1; domain=1; HttpOnly', 34 "nice_cookie" => "!nice_value",
34 'Set-Cookie: nice_cookie=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ8ko%2ByA4y%2Bmw5MGBx8fgc3TWOAvhIu%2BfF%2Bx2g%3D%3D; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=1; samesite=Strict; domain=1; secure; HttpOnly', 35 "super_cookie" => "super_value",
35 ); 36);
36 37
37$headers = headers_list(); 38$headers = headers_list();
38if (($i = count($expected)) > count($headers)) 39if (($i = count($expected)) > count($headers))
@@ -41,31 +42,37 @@ if (($i = count($expected)) > count($headers))
41 return; 42 return;
42} 43}
43 44
44do 45$i = 0;
45{ 46
47do {
46 if (strncmp(current($headers), 'Set-Cookie:', 11) !== 0) 48 if (strncmp(current($headers), 'Set-Cookie:', 11) !== 0)
47 { 49 {
48 continue; 50 continue;
49 } 51 }
50 52 foreach ($expected as $key => $value) {
51 if (current($headers) === current($expected)) 53 if (strpos(current($headers), $key) !== false) { // If the header contains the cookie
52 { 54 if (substr($value, 0, 1) === "!") { // ! is because we don't want to see the cookie value in plaintext, it must be encrypted
53 $i--; 55 if (strpos(current($headers), substr($value,1,-1)) === false) { // If the header doesn't contain de cookie value, it's good
56 $i++;
57 break;
58 }
59 echo "Received : " . current($headers) . " and the cookie isn't encrypted . \n";
60 } else {
61 if (strpos(current($headers), $value) !== false) {
62 $i++;
63 break;
64 }
65 echo "Received : " . current($headers) . " and the cookie value of " . $key . " doesn't match it's value. \n";
66 }
67 break;
68 }
54 } 69 }
55 else
56 {
57 echo "Header mismatch:\n\tExpected: "
58 .current($expected)
59 ."\n\tReceived: ".current($headers)."\n";
60 }
61
62 next($expected);
63} 70}
64while (next($headers) !== FALSE); 71while (next($headers));
65 72
66echo ($i === 0) 73echo ($i === 4)
67 ? 'OK' 74 ? 'OK'
68 : 'A total of '.$i.' errors found.'; 75 : 'A total of '. (count($expected) - $i) .' errors found.';
69?> 76?>
70--EXPECT-- 77--EXPECT--
71OK 78OK