sanitizing and security
authorPaul Hänsch <paul@plutz.net>
Tue, 2 Jun 2020 02:49:55 +0000 (04:49 +0200)
committerPaul Hänsch <paul@plutz.net>
Tue, 2 Jun 2020 02:49:55 +0000 (04:49 +0200)
cgilite.sh

index 1d8d30a..384ec19 100755 (executable)
@@ -27,19 +27,18 @@ BR='
 '
 cgilite_timeout=2
 
-HEADER(){
-  # Read value of header line. Use this instead of
-  # referencing HTTP_* environment variables.
-  if [ -n "${cgilite_headers+x}" ]; then
-    local h="${BR}${cgilite_headers}"
-    [ "${h##*${BR}${1}: }" = "${h}" ] && return 1
-    h="${h##*${BR}${1}: }" h="${h%%${BR}*}"
-    printf %s "${h%${CR}}"
-  else
-    eval "printf %s \"\$HTTP_$(printf %s "${1}" |tr a-z A-Z |tr -c A-Z _)\""
-  fi
+PATH(){
+  { [ $# -eq 0 ] && cat || printf %s "$*"; } \
+  | sed -E 's;^.*$;/&/;; s;/+;/;g;
+            :X;
+            s;^/\.\./;/;; s;/\./;/;g;
+            tX;
+            s;/[^/]+/\.\./;/;;
+            tX;
+            s;^(/.*)/$;\1;'
 }
 
+
 HEX_DECODE='
   s;\\;\\\\;g; :HEXDECODE_X; s;%([^0-9A-F]);\\045\1;g; tHEXDECODE_X;
   # Hexadecimal { %00 - %FF } will be transformed to octal { \000 - \377 } for posix printf
@@ -70,19 +69,23 @@ if [ -z "$REQUEST_METHOD" ]; then
 
   (sleep $cgilite_timeout && kill $$) & cgilite_watchdog=$!
   while read REQUEST_METHOD REQUEST_URI SERVER_PROTOCOL; do
+    [ "${SERVER_PROTOCOL#HTTP/1.[01]${CR}}" ] && break
     kill $cgilite_watchdog
-    PATH_INFO="$(HEX_DECODE "${REQUEST_URI%\?*}")"
+
+    SERVER_PROTOCOL="${SERVER_PROTOCOL%${CR}}"
+    PATH_INFO="$(HEX_DECODE "${REQUEST_URI%\?*}" |PATH)"
     QUERY_STRING="${REQUEST_URI#*\?}"
     cgilite_headers=''; while read -r hl; do
-      [ "${hl%${CR}}" ] || break
+      hl="${hl%${CR}}"; [ "$hl" ] || break
+      case $hl in
+        'Content-Length: '*) CONTENT_LENGTH="${hl#*: }";;
+          'Content-Type: '*)   CONTENT_TYPE="${hl#*: }";;
+      esac
       cgilite_headers="${cgilite_headers}${hl}${BR}"
     done
 
-    HTTP_CONTENT_LENGTH="$(HEADER Content-Length)"
-    HTTP_CONTENT_LENGTH="${HTTP_CONTENT_LENGTH##*[^0-9]}"
-
     export REMOTE_ADDR SERVER_NAME SERVER_PORT REQUEST_METHOD REQUEST_URI SERVER_PROTOCOL \
-           PATH_INFO QUERY_STRING HTTP_CONTENT_LENGTH
+           PATH_INFO QUERY_STRING CONTENT_TYPE CONTENT_LENGTH
 
     # Try to serve multiple requests, provided that script serves a
     # Content-Length header.
@@ -106,11 +109,12 @@ if [ -z "$REQUEST_METHOD" ]; then
   exit 0
 fi
 
-if [ "$REQUEST_METHOD" = POST -a "${HTTP_CONTENT_LENGTH:=${CONTENT_LENGTH:=0}}" -gt 0 ]; then
-  cgilite_post="$(head -c "$HTTP_CONTENT_LENGTH")"
+if [ "${REQUEST_METHOD}" = POST -a "${CONTENT_LENGTH:-0}" -gt 0 -a \
+     "${CONTENT_TYPE}" = "application/x-www-form-urlencoded" ]; then
+  cgilite_post="$(head -c "$CONTENT_LENGTH")"
 fi
 
-[ -n "${DEBUG+x}" ] && env
+[ "${DEBUG+x}" ] && env >&2
 
 cgilite_count(){
   printf %s "&$1" \
@@ -121,7 +125,8 @@ cgilite_count(){
 cgilite_value(){
   local str="&$1" name="$2" cnt="${3:-1}"
   while [ $cnt -gt 0 ]; do
-    str=${str#*&${name}=}
+    [ "${str}" = "${str#*&${name}=}" ] && return 1
+    str="${str#*&${name}=}"
     cnt=$((cnt - 1))
   done
   printf -- "$(printf %s "${str%%&*}" |sed -E 's;\+; ;g;'"$HEX_DECODE")"
@@ -148,6 +153,21 @@ REF(){ cgilite_value "${HTTP_REFERER#*\?}" $@; }
 REF_COUNT(){ cgilite_count "${HTTP_REFERER#*\?}" $1; }
 REF_KEYS(){ cgilite_keys "${HTTP_REFERER#*\?}"; }
 
+HEADER(){
+  # Read value of header line. Use this instead of
+  # referencing HTTP_* environment variables.
+  if [ -n "${cgilite_headers+x}" ]; then
+    local str="${BR}${cgilite_headers}"
+    [ "${str}" = "${str#*${BR}${1}: }" ] && return 1
+    str="${str#*${BR}${1}: }"
+    printf %s "${str%%${BR}*}"
+  else
+    local var="HTTP_$(printf %s "$1" |tr a-z- A-Z-)"
+    eval "[ \"\$$var\" ] && printf %s \"\$$var\" || return 1"
+    # eval "printf %s \"\$HTTP_$(printf %s "${1}" |tr a-z A-Z |tr -c A-Z _)\""
+  fi
+}
+
 COOKIE(){
   HEX_DECODE "$(
     HEADER Cookie \
@@ -188,19 +208,10 @@ URL(){
   | sed 's;,;%;g; s;%2F;/;g;'
 }
 
-PATH(){
-  { [ $# -eq 0 ] && cat || printf %s "$*"; } \
-  | sed -E 's;^.*$;/&/;; s;/+;/;g;
-            :X;
-            s;^/\.\./;/;; s;/\./;/;g;
-            tX;
-            s;/[^/]+/\.\./;/;;
-            tX;
-            s;^(/.*)/$;\1;'
-}
-
-
 SET_COOKIE(){
+  # Param: session | +seconds | [date]
+  # Param: name=value
+  # Param: Path= | Domain= | Secure
   local expire cookie
   case "$1" in
     ''|0|session) expire='';;
@@ -209,7 +220,7 @@ SET_COOKIE(){
   esac
   cookie="$2"
 
-  printf 'Set-Cookie: %s' "$cookie"
+  printf 'Set-Cookie: %s; HttpOnly; SameSite=Lax' "$cookie"
   [ -n "$expire" ] && printf '; Expires=%s' "${expire%+????}${expire:+GMT}"
   [ $# -ge 3 ] && shift 2 && printf '; %s' "$@"
   printf '\r\n'