@cryptotaxi247 / netdata-1 / commits / 2b797b060

added X-Frame-Options HTTP header support; fixes #1735

Costa Tsaousis (ktsaou) committed Feb 15, 2017 at 21:56 UTC 2b797b0600c65a8fe5a8b839863ec7cb0e98fe3d
3 files changed +156 -142
src/main.c
+3 -1
@@ -71,7 +71,9 @@ void web_server_threading_selection(void) {
71
72 web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
73
74 - web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
74 + respect_web_browser_do_not_track_policy = config_get_boolean("global", "respect web browser do not track policy", respect_web_browser_do_not_track_policy);
75 + web_x_frame_options = config_get("global", "web x-frame-options header", "");
76 + if(!*web_x_frame_options) web_x_frame_options = NULL;
77
78 #ifdef NETDATA_WITH_ZLIB
79 web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
src/web_client.c
+147 -140
@@ -5,7 +5,8 @@
5 #define TOO_BIG_REQUEST 16384
6
7 int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
8 -int web_donotrack_comply = 0;
8 +int respect_web_browser_do_not_track_policy = 0;
9 +char *web_x_frame_options = NULL;
10
11 #ifdef NETDATA_WITH_ZLIB
12 int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
@@ -1347,7 +1348,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
1348 #endif /* NETDATA_INTERNAL_CHECKS */
1349 }
1350
1350 - if(web_donotrack_comply && w->donottrack) {
1351 + if(respect_web_browser_do_not_track_policy && w->donottrack) {
1352 buffer_flush(w->response.data);
1353 buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
1354 return 400;
@@ -1824,7 +1825,7 @@ static inline char *http_header_parse(struct web_client *w, char *s) {
1825 if(strcasestr(v, "keep-alive"))
1826 w->keepalive = 1;
1827 }
1827 - else if(web_donotrack_comply && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
1828 + else if(respect_web_browser_do_not_track_policy && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
1829 if(*v == '0') w->donottrack = 0;
1830 else if(*v == '1') w->donottrack = 1;
1831 }
@@ -1926,6 +1927,148 @@ static inline int http_request_validate(struct web_client *w) {
1927 return -3;
1928 }
1929
1930 +static inline void web_client_send_http_header(struct web_client *w) {
1931 + if(unlikely(w->response.code != 200))
1932 + buffer_no_cacheable(w->response.data);
1933 +
1934 + // set a proper expiration date, if not already set
1935 + if(unlikely(!w->response.data->expires)) {
1936 + if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
1937 + w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
1938 + else
1939 + w->response.data->expires = w->tv_ready.tv_sec + 86400;
1940 + }
1941 +
1942 + // prepare the HTTP response header
1943 + debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.code);
1944 +
1945 + const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
1946 + const char *code_msg = web_response_code_to_string(w->response.code);
1947 +
1948 + // prepare the last modified and expiration dates
1949 + char date[32], edate[32];
1950 + {
1951 + struct tm tmbuf, *tm;
1952 +
1953 + tm = gmtime_r(&w->response.data->date, &tmbuf);
1954 + strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
1955 +
1956 + tm = gmtime_r(&w->response.data->expires, &tmbuf);
1957 + strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
1958 + }
1959 +
1960 + buffer_sprintf(w->response.header_output,
1961 + "HTTP/1.1 %d %s\r\n"
1962 + "Connection: %s\r\n"
1963 + "Server: NetData Embedded HTTP Server\r\n"
1964 + "Access-Control-Allow-Origin: %s\r\n"
1965 + "Access-Control-Allow-Credentials: true\r\n"
1966 + "Content-Type: %s\r\n"
1967 + "Date: %s\r\n"
1968 + , w->response.code, code_msg
1969 + , w->keepalive?"keep-alive":"close"
1970 + , w->origin
1971 + , content_type_string
1972 + , date
1973 + );
1974 +
1975 + if(unlikely(web_x_frame_options))
1976 + buffer_sprintf(w->response.header_output, "X-Frame-Options: %s\r\n", web_x_frame_options);
1977 +
1978 + if(w->cookie1[0] || w->cookie2[0]) {
1979 + if(w->cookie1[0]) {
1980 + buffer_sprintf(w->response.header_output,
1981 + "Set-Cookie: %s\r\n",
1982 + w->cookie1);
1983 + }
1984 +
1985 + if(w->cookie2[0]) {
1986 + buffer_sprintf(w->response.header_output,
1987 + "Set-Cookie: %s\r\n",
1988 + w->cookie2);
1989 + }
1990 +
1991 + if(respect_web_browser_do_not_track_policy)
1992 + buffer_sprintf(w->response.header_output,
1993 + "Tk: T;cookies\r\n");
1994 + }
1995 + else {
1996 + if(respect_web_browser_do_not_track_policy) {
1997 + if(w->tracking_required)
1998 + buffer_sprintf(w->response.header_output,
1999 + "Tk: T;cookies\r\n");
2000 + else
2001 + buffer_sprintf(w->response.header_output,
2002 + "Tk: N\r\n");
2003 + }
2004 + }
2005 +
2006 + if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
2007 + buffer_strcat(w->response.header_output,
2008 + "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
2009 + "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
2010 + "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
2011 + );
2012 + }
2013 + else {
2014 + buffer_sprintf(w->response.header_output,
2015 + "Cache-Control: %s\r\n"
2016 + "Expires: %s\r\n",
2017 + (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
2018 + edate);
2019 + }
2020 +
2021 + // copy a possibly available custom header
2022 + if(unlikely(buffer_strlen(w->response.header)))
2023 + buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
2024 +
2025 + // headers related to the transfer method
2026 + if(likely(w->response.zoutput)) {
2027 + buffer_strcat(w->response.header_output,
2028 + "Content-Encoding: gzip\r\n"
2029 + "Transfer-Encoding: chunked\r\n"
2030 + );
2031 + }
2032 + else {
2033 + if(likely((w->response.data->len || w->response.rlen))) {
2034 + // we know the content length, put it
2035 + buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
2036 + }
2037 + else {
2038 + // we don't know the content length, disable keep-alive
2039 + w->keepalive = 0;
2040 + }
2041 + }
2042 +
2043 + // end of HTTP header
2044 + buffer_strcat(w->response.header_output, "\r\n");
2045 +
2046 + // sent the HTTP header
2047 + debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
2048 + , w->id
2049 + , buffer_strlen(w->response.header_output)
2050 + , buffer_tostring(w->response.header_output)
2051 + );
2052 +
2053 + web_client_crock_socket(w);
2054 +
2055 + ssize_t bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
2056 + if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
2057 + if(bytes > 0)
2058 + w->stats_sent_bytes += bytes;
2059 +
2060 + debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
2061 + , w->id
2062 + , buffer_strlen(w->response.header_output)
2063 + , bytes);
2064 +
2065 + WEB_CLIENT_IS_DEAD(w);
2066 + return;
2067 + }
2068 + else
2069 + w->stats_sent_bytes += bytes;
2070 +}
2071 +
2072 void web_client_process(struct web_client *w) {
2073 static uint32_t
2074 hash_api = 0,
@@ -1959,7 +2102,6 @@ void web_client_process(struct web_client *w) {
2102 }
2103
2104 int code = 500;
1962 - ssize_t bytes;
2105
2106 int what_to_do = http_request_validate(w);
2107
@@ -2160,142 +2302,7 @@ void web_client_process(struct web_client *w) {
2302 if(unlikely(!w->response.data->date))
2303 w->response.data->date = w->tv_ready.tv_sec;
2304
2163 - if(unlikely(code != 200))
2164 - buffer_no_cacheable(w->response.data);
2165 -
2166 - // set a proper expiration date, if not already set
2167 - if(unlikely(!w->response.data->expires)) {
2168 - if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
2169 - w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
2170 - else
2171 - w->response.data->expires = w->tv_ready.tv_sec + 86400;
2172 - }
2173 -
2174 - // prepare the HTTP response header
2175 - debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
2176 -
2177 - const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
2178 - const char *code_msg = web_response_code_to_string(code);
2179 -
2180 - // prepare the last modified and expiration dates
2181 - char date[32], edate[32];
2182 - {
2183 - struct tm tmbuf, *tm;
2184 -
2185 - tm = gmtime_r(&w->response.data->date, &tmbuf);
2186 - strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
2187 -
2188 - tm = gmtime_r(&w->response.data->expires, &tmbuf);
2189 - strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
2190 - }
2191 -
2192 - buffer_sprintf(w->response.header_output,
2193 - "HTTP/1.1 %d %s\r\n"
2194 - "Connection: %s\r\n"
2195 - "Server: NetData Embedded HTTP Server\r\n"
2196 - "Access-Control-Allow-Origin: %s\r\n"
2197 - "Access-Control-Allow-Credentials: true\r\n"
2198 - "Content-Type: %s\r\n"
2199 - "Date: %s\r\n"
2200 - , code, code_msg
2201 - , w->keepalive?"keep-alive":"close"
2202 - , w->origin
2203 - , content_type_string
2204 - , date
2205 - );
2206 -
2207 - if(w->cookie1[0] || w->cookie2[0]) {
2208 - if(w->cookie1[0]) {
2209 - buffer_sprintf(w->response.header_output,
2210 - "Set-Cookie: %s\r\n",
2211 - w->cookie1);
2212 - }
2213 -
2214 - if(w->cookie2[0]) {
2215 - buffer_sprintf(w->response.header_output,
2216 - "Set-Cookie: %s\r\n",
2217 - w->cookie2);
2218 - }
2219 -
2220 - if(web_donotrack_comply)
2221 - buffer_sprintf(w->response.header_output,
2222 - "Tk: T;cookies\r\n");
2223 - }
2224 - else {
2225 - if(web_donotrack_comply) {
2226 - if(w->tracking_required)
2227 - buffer_sprintf(w->response.header_output,
2228 - "Tk: T;cookies\r\n");
2229 - else
2230 - buffer_sprintf(w->response.header_output,
2231 - "Tk: N\r\n");
2232 - }
2233 - }
2234 -
2235 - if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
2236 - buffer_strcat(w->response.header_output,
2237 - "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
2238 - "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
2239 - "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
2240 - );
2241 - }
2242 - else {
2243 - buffer_sprintf(w->response.header_output,
2244 - "Cache-Control: %s\r\n"
2245 - "Expires: %s\r\n",
2246 - (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
2247 - edate);
2248 - }
2249 -
2250 - // copy a possibly available custom header
2251 - if(unlikely(buffer_strlen(w->response.header)))
2252 - buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
2253 -
2254 - // headers related to the transfer method
2255 - if(likely(w->response.zoutput)) {
2256 - buffer_strcat(w->response.header_output,
2257 - "Content-Encoding: gzip\r\n"
2258 - "Transfer-Encoding: chunked\r\n"
2259 - );
2260 - }
2261 - else {
2262 - if(likely((w->response.data->len || w->response.rlen))) {
2263 - // we know the content length, put it
2264 - buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
2265 - }
2266 - else {
2267 - // we don't know the content length, disable keep-alive
2268 - w->keepalive = 0;
2269 - }
2270 - }
2271 -
2272 - // end of HTTP header
2273 - buffer_strcat(w->response.header_output, "\r\n");
2274 -
2275 - // sent the HTTP header
2276 - debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
2277 - , w->id
2278 - , buffer_strlen(w->response.header_output)
2279 - , buffer_tostring(w->response.header_output)
2280 - );
2281 -
2282 - web_client_crock_socket(w);
2283 -
2284 - bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
2285 - if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
2286 - if(bytes > 0)
2287 - w->stats_sent_bytes += bytes;
2288 -
2289 - debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
2290 - , w->id
2291 - , buffer_strlen(w->response.header_output)
2292 - , bytes);
2293 -
2294 - WEB_CLIENT_IS_DEAD(w);
2295 - return;
2296 - }
2297 - else
2298 - w->stats_sent_bytes += bytes;
2305 + web_client_send_http_header(w);
2306
2307 // enable sending immediately if we have data
2308 if(w->response.data->len) w->wait_send = 1;
src/web_client.h
+6 -1
@@ -5,9 +5,14 @@
5 extern int web_client_timeout;
6
7 #ifdef NETDATA_WITH_ZLIB
8 -extern int web_enable_gzip, web_gzip_level, web_gzip_strategy, web_donotrack_comply;
8 +extern int web_enable_gzip,
9 + web_gzip_level,
10 + web_gzip_strategy;
11 #endif /* NETDATA_WITH_ZLIB */
12
13 +extern int respect_web_browser_do_not_track_policy;
14 +extern char *web_x_frame_options;
15 +
16 #define WEB_CLIENT_MODE_NORMAL 0
17 #define WEB_CLIENT_MODE_FILECOPY 1
18 #define WEB_CLIENT_MODE_OPTIONS 2