Class: Aikido::Zen::Scanners::SQLInjectionScanner

Inherits:
Object
  • Object
show all
Defined in:
lib/aikido/zen/scanners/sql_injection_scanner.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query, input, dialect) ⇒ SQLInjectionScanner



55
56
57
58
59
# File 'lib/aikido/zen/scanners/sql_injection_scanner.rb', line 55

def initialize(query, input, dialect)
  @query = query.downcase
  @input = input.downcase.strip
  @dialect = dialect
end

Instance Attribute Details

#failed_to_tokenizeObject (readonly)

Returns the value of attribute failed_to_tokenize.



53
54
55
# File 'lib/aikido/zen/scanners/sql_injection_scanner.rb', line 53

def failed_to_tokenize
  @failed_to_tokenize
end

Class Method Details

.call(query:, dialect:, scan:, sink:, context:, operation:) ⇒ Aikido::Zen::Attack?

Checks if the given SQL query may have dangerous user input injected, and returns an Attack if so, based on the current request.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/aikido/zen/scanners/sql_injection_scanner.rb', line 26

def self.call(query:, dialect:, scan:, sink:, context:, operation:)
  dialect = Aikido::Zen::SQL::Dialects.fetch(dialect)

  context.payloads.each do |payload|
    scanner = new(query, payload.value.to_s, dialect)
    next unless scanner.attack?

    return Attacks::SQLInjectionAttack.new(
      sink: sink,
      query: query,
      input: payload,
      dialect: dialect,
      context: context,
      operation: "#{sink.operation}.#{operation}",
      stack: Aikido::Zen.clean_stack_trace,
      failed_to_tokenize: scanner.failed_to_tokenize
    )
  rescue Aikido::Zen::InternalsError => error
    Aikido::Zen.config.logger.warn(error.message)
    scan.track_error(error, self)
  rescue => error
    scan.track_error(error, self)
  end

  nil
end

.skips_on_nil_context?Boolean



9
10
11
# File 'lib/aikido/zen/scanners/sql_injection_scanner.rb', line 9

def self.skips_on_nil_context?
  true
end

Instance Method Details

#attack?Boolean



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/aikido/zen/scanners/sql_injection_scanner.rb', line 61

def attack?
  # Ignore single char inputs since they shouldn't be able to do much harm
  return false if @input.length <= 1

  # If the input is longer than the query, then it is not part of it
  return false if @input.length > @query.length

  # If the input is not included in the query at all, then we are safe
  return false unless @query.include?(@input)

  # If the input is solely alphanumeric, we can ignore it
  return false if Aikido::Zen::Helpers.regexp_with_timeout(/\A[[:alnum:]_]+\z/i).match?(@input)

  # If the input is a comma-separated list of numbers, ignore it.
  return false if Aikido::Zen::Helpers.regexp_with_timeout(/\A[ ,]*\d[ ,\d]*\z/).match?(@input)

  result = Internals.detect_sql_injection(@query, @input, @dialect)

  case result
  when 0
    false
  when 1
    true
  when 3
    @failed_to_tokenize = true
    Aikido::Zen.config.block_invalid_sql?
  end
rescue => err
  return true if defined?(Regexp::TimeoutError) && err.is_a?(Regexp::TimeoutError)

  raise err
end