Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to write REGEX for username validation in Rails

I'm trying to write a regular expression in Ruby (Rails) so that a username's characters only contains numbers and letters (also no spaces).

I have this regex, /^[a-zA-Z0-9]+$/, but it doesn't seem to be working and I get an error in Rails that says "The provided regular expression is using multiline anchors (^ or $), which may present a security risk. Did you mean to use \A and \z, or forgot to add the :multiline => true option?"

My full code for this implementation in my user.rb model is:

class User < ActiveRecord::Base
  before_save { self.email = email.downcase }
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_USERNAME_REGEX = /^[a-zA-Z0-9]+$/
  validates :username, presence: true, length: { maximum: 20 },
                                    format: { with: VALID_USERNAME_REGEX },
                                    uniqueness: { case_sensitive: false }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                format: { with: VALID_EMAIL_REGEX },
                uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, length: { minimum: 6 }
end

What am I doing wrong and how I can fix this regex so that it only is valid for numbers and letters and no spaces? Thanks.

like image 839
jtarr523 Avatar asked Nov 13 '14 02:11

jtarr523


2 Answers

Short answer: use /\A[a-zA-Z0-9]+\z/ instead (as VALID_EMAIL_REGEX is using).

Long answer: the ^ and $ anchors will match the beginning and end of a line in a string. That means that if your string consists of multiple lines of alphanumeric characters they will match:

/^[a-zA-Z0-9]+$/ =~ "Ana\nBob\nClara\nDaniel"  #=> 0 (matches)

The \A and \z on the other hand will match the beginning and end of a string, hence it will prevent a possible attack by a user sending a multiline string (like in the previous example).

/\A[a-zA-Z0-9]+\z/ =~ "Ana\nBob\nClara\nDaniel"  #=> nil (doesn't match)
/\A[a-zA-Z0-9]+\z/ =~ "Erika"                    #=> 0   (matches)
like image 134
Gabriel de Oliveira Avatar answered Sep 20 '22 18:09

Gabriel de Oliveira


All you have to do is follow the error message. Replace ^ (start of line anchor) with \A (start of string anchor), and $ (end of line anchor) with \z (end of string anchor). Other than that, your regex works as is.

\A[a-zA-Z0-9]+\z

Rails has this security concern because unlike some languages, ^ and $ only match the beginning/end of a single line, rather than the entire string.

This illustrates an example of this possible exploit:

str = "malicious_code()\naValidUsername"
str.match(/^[a-zA-Z0-9]+$/) # => #<MatchData "aValidUsername">
like image 36
August Avatar answered Sep 20 '22 18:09

August