Skip to content

Conversation

@alexrford
Copy link
Contributor

@alexrford alexrford commented Jan 23, 2022

Tracks cases where potentially sensitive data (e.g. passwords and other credentials) are logged as cleartext.

This is loosely based on the JS version of the same query, using the same SensitiveDataHeuristics.qll and a similar structure for the CleartextLoggingCustomizations.qll.

@alexrford alexrford added WIP This is a work-in-progress, do not merge yet! Ruby labels Jan 23, 2022
@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login from (user: #{username}, password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login from (user: #{username}, password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

1 similar comment
@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login from (user: #{username}, password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login from (user: #{username}, password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@alexrford alexrford removed the WIP This is a work-in-progress, do not merge yet! label Jan 28, 2022
@alexrford alexrford changed the title Ruby: Add rb/clear-text-logging-sensitive-data query [WIP] Ruby: Add rb/clear-text-logging-sensitive-data query Jan 28, 2022
@alexrford alexrford force-pushed the ruby/clear-text-logging branch from 14a4919 to 269722f Compare January 28, 2022 17:28
@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login with password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login with password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login with password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login with password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@alexrford alexrford marked this pull request as ready for review January 28, 2022 18:43
@alexrford alexrford requested a review from a team as a code owner January 28, 2022 18:43
hvitved
hvitved previously requested changes Feb 3, 2022
Copy link
Contributor

@hmac hmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a few minor comments and questions. In general this looks super thorough, but I have trouble following some of the logic in CleartextLoggingCustomizations.qll, I think because we deal with heuristics both for "sensitive" data and "not sensitive" data. Hopefully a second read through will make it a bit clearer for me.

*/
private class ObfuscatorCall extends Sanitizer, DataFlow::CallNode {
ObfuscatorCall() {
this.getMethodName().regexpMatch(notSensitiveRegexp()) and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we can use notSensitiveRegexp as-is. It seems to flag any method with a ! in it, which is of course a common suffix for Ruby method names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I encountered similar problems with [] and []= which I ended up special-casing using nonSensitiveMethodNameExclusion. The problem here is the [^\\w$.-] sub-clause that marks most methods containing non-alphanumerics as being "not sensitive".

In this case I think it could be sufficient to have ! as a suffix as another special exception for Ruby (and also to use nonSensitiveMethodNameExclusion more consistently in this file)? The other common naming pattern that I can think of is a ? suffix for predicate methods, which probably make sense as being marked as non-sensitive.

override predicate isSink(DataFlow::Node sink) { sink instanceof CleartextLogging::Sink }

override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TaintTracking::Configuration.isSanitizer(_) is none(), so isn't this line redundant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added it in case we extend the default isSanitizer predicate in the future, though I haven't been consistent in doing this with all dataflow/taint tracking queries.

/** Holds if `nodeFrom` taints `nodeTo`. */
predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(string name, ElementReference ref, LocalVariable hashVar |
// from `hsh[password] = "changeme"` to a `hsh[password]` read
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this additional step be dropped once we support flow through hashes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I expect so.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 8, 2022

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login with password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login with password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login with password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login with password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login with password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login with password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@alexrford alexrford requested a review from hmac February 16, 2022 10:24
hmac
hmac previously approved these changes Feb 17, 2022
Copy link
Contributor

@hmac hmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. I understand it better on the second read through!

I've left one comment about a possible simiplifcation, but otherwise 👍. I've also kicked off a DCA run that should be done by the time you read this.

def.hasAdjacentReads(any(MaskingReplacerSanitizedNode read).asExpr(), this.asExpr())
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be simplified by replacing it with something like this:

  /**
   * Like `MaskingReplacerSanitizer` but updates the receiver.
   * Taint is thereby cleared for any subsequent read.
   */
  private class InPlaceMaskingReplacerSanitizer extends Sanitizer {
    InPlaceMaskingReplacerSanitizer() {
      exists(MaskingReplacerSanitizer m | m.getMethodName() = ["gsub!", "sub!"] |
        m.getReceiver() = this
      )
    }
  }

The idea being that we make the receiver of sub! be a sanitizer, which (I believe) removes it and any subsequent reads of the same variable from the dataflow graph.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's really nice - much neater and truer to the intent of this sanitizer.

@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login with password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login with password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@github-actions
Copy link
Contributor

QHelp previews:

ruby/ql/src/queries/security/cwe-312/CleartextLogging.qhelp

Clear-text logging of sensitive information

Sensitive information that is stored unencrypted is accessible to an attacker who gains access to the storage.

Recommendation

Ensure that sensitive information is always encrypted before being stored.

In general, decrypt sensitive information only at the point where it is necessary for it to be used in cleartext.

Be aware that external processes often store the standard out and standard error streams of the application, causing logged sensitive information to be stored as well.

Example

The following example code logs user credentials (in this case, their password) to standard out in plaintext:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    @@logger.info "login with password: #{password})"
  end
end

Instead, the credentials should be masked or redacted before logging:

require 'Logger'

class UserSession
  @@logger = Logger.new STDOUT

  def login(username, password)
    # ...
    password_escaped = password.sub(/.*/, "[redacted]")
    @@logger.info "login with password: #{password_escaped})"
  end
end

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@alexrford alexrford merged commit 746290d into main Feb 21, 2022
@alexrford alexrford deleted the ruby/clear-text-logging branch February 21, 2022 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants