# frozen_string_literal: true # # Copyright (c) 2023-2024 Hal Brodigan (postmodern.mod3@gmail.com) # # ronin-web-session_cookie is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ronin-web-session_cookie is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ronin-web-session_cookie. If not, see . # require 'ronin/web/session_cookie/cookie' require 'base64' require 'json' module Ronin module Web module SessionCookie # # Represents a [JSON Web Token (JWT)][JWT]. # # [JWT]: https://jwt.io # # ## Examples # # Ronin::Web::SessionCookie.parse('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c') # # => # # #"HS256", "typ"=>"JWT"}, # # @hmac= # # ":\x93\x92K\x0E\xDE\xE3\xCEK8\xFEO\xAF4\x9C\xC4v\xFBI\x1E\xAC\x00\xE3\x11rG\xC5\xC2.+\xA7\xBA", # # @params={"id"=>123456789, "name"=>"Joseph"}> # # @see https://jwt.io/ # class JWT < Cookie # The parsed JWT header information. # # @return [Hash{String => Object}] # # @api public attr_reader :header # The SHA256 HMAC of the encoded {#header} + `.` + the encoded # {#payload}. # # @return [String] # # @api public attr_reader :hmac alias payload params # # Initializes the parsed JWT session cookie. # # @param [Hash{String => Object}] header # The parsed header information. # # @param [Hash{String => Object}] payload # The parsed JWT payload. # # @param [String] hmac # The SHA256 HMAC of the encoded header + `.` + the encoded payload. # # @api private # def initialize(header,payload,hmac) @header = header super(payload) @hmac = hmac end # Regular expression to match JWT session cookies. REGEXP = /\A(Bearer )?#{URL_SAFE_BASE64_REGEXP}\.#{URL_SAFE_BASE64_REGEXP}\.#{URL_SAFE_BASE64_REGEXP}\z/ # # Identifies whether the string is a JWT session cookie. # # @param [String] string # The raw session cookie value to identify. # # @return [Boolean] # Indicates whether the session cookie value is a JWT session cookie. # # @api public # def self.identify?(string) string =~ REGEXP end # # Parses a JWT session cookie. # # @param [String] string # The raw session cookie string to parse. # # @return [JWT] # The parsed and deserialized session cookie # # @api public # def self.parse(string) # remove any 'Bearer ' prefix. string = string.sub(/\ABearer /,'') # split the string header, payload, hmac = string.split('.',3) header = JSON.parse(Base64.decode64(header)) payload = JSON.parse(Base64.decode64(payload)) hmac = Base64.decode64(hmac) return new(header,payload,hmac) end # # Extracts the JWT session cookie from the HTTP response. # # @param [Net::HTTPResponse] response # The HTTP response object. # # @return [JWT, nil] # The parsed JWT session cookie, or `nil` if there was no # `Authorization` header containing a JWT session cookie. # # @api public # def self.extract(response) if (authorization = response['Authorization']) if (match = authorization.match(REGEXP)) return parse(match[0]) end end end end end end end