52 votes

Ruby on Rails "séquence d'octets invalide en UTF-8" due à un bot

J'ai des erreurs déclenchées par un robot chinois : http://www.easou.com/search/spider.html quand il fait défiler mes sites web.

Les versions de mes applications sont toutes avec Ruby 1.9.3 et Rails 3.2.X

Voici une trace de la pile :

An ArgumentError occurred in listings#show:

  invalid byte sequence in UTF-8
  rack (1.4.5) lib/rack/utils.rb:104:in `normalize_params'

-------------------------------
Request:
-------------------------------

  * URL       : http://www.my-website.com
  * IP address: X.X.X.X
  * Parameters: {"action"=>"show", "controller"=>"listings", "id"=>"location-t7-villeurbanne--58"}
  * Rails root: /.../releases/20140708150222
  * Timestamp : 2014-07-09 02:57:43 +0200

-------------------------------
Backtrace:
-------------------------------

  rack (1.4.5) lib/rack/utils.rb:104:in `normalize_params'
  rack (1.4.5) lib/rack/utils.rb:96:in `block in parse_nested_query'
  rack (1.4.5) lib/rack/utils.rb:93:in `each'
  rack (1.4.5) lib/rack/utils.rb:93:in `parse_nested_query'
  rack (1.4.5) lib/rack/request.rb:332:in `parse_query'
  actionpack (3.2.18) lib/action_dispatch/http/request.rb:275:in `parse_query'
  rack (1.4.5) lib/rack/request.rb:209:in `POST'
  actionpack (3.2.18) lib/action_dispatch/http/request.rb:237:in `POST'
  actionpack (3.2.18) lib/action_dispatch/http/parameters.rb:10:in `parameters'

-------------------------------
Session:
-------------------------------

  * session id: nil
  * data: {}

-------------------------------
Environment:
-------------------------------

  * CONTENT_LENGTH                                 : 514
  * CONTENT_TYPE                                   : application/x-www-form-urlencoded
  * HTTP_ACCEPT                                    : text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
  * HTTP_ACCEPT_ENCODING                           : gzip, deflate
  * HTTP_ACCEPT_LANGUAGE                           : zh;q=0.9,en;q=0.8
  * HTTP_CONNECTION                                : close
  * HTTP_HOST                                      : www.my-website.com
  * HTTP_REFER                                     : http://www.my-website.com/
  * HTTP_USER_AGENT                                : Mozilla/5.0 (compatible; EasouSpider; +http://www.easou.com/search/spider.html)
  * ORIGINAL_FULLPATH                              : /
  * PASSENGER_APP_SPAWNER_IDLE_TIME                : -1
  * PASSENGER_APP_TYPE                             : rack
  * PASSENGER_CONNECT_PASSWORD                     : [FILTERED]
  * PASSENGER_DEBUGGER                             : false
  * PASSENGER_ENVIRONMENT                          : production
  * PASSENGER_FRAMEWORK_SPAWNER_IDLE_TIME          : -1
  * PASSENGER_FRIENDLY_ERROR_PAGES                 : true
  * PASSENGER_GROUP                                :
  * PASSENGER_MAX_REQUESTS                         : 0
  * PASSENGER_MIN_INSTANCES                        : 1
  * PASSENGER_SHOW_VERSION_IN_HEADER               : true
  * PASSENGER_SPAWN_METHOD                         : smart-lv2
  * PASSENGER_USER                                 :
  * PASSENGER_USE_GLOBAL_QUEUE                     : true
  * PATH_INFO                                      : /
  * QUERY_STRING                                   :
  * REMOTE_ADDR                                    : 183.60.212.153
  * REMOTE_PORT                                    : 52997
  * REQUEST_METHOD                                 : GET
  * REQUEST_URI                                    : /
  * SCGI                                           : 1
  * SCRIPT_NAME                                    :
  * SERVER_PORT                                    : 80
  * SERVER_PROTOCOL                                : HTTP/1.1
  * SERVER_SOFTWARE                                : nginx/1.2.6
  * UNION_STATION_SUPPORT                          : false
  * _                                              : _
  * action_controller.instance                     : listings#show
  * action_dispatch.backtrace_cleaner              : #<Rails::BacktraceCleaner:0x000000056e8660>
  * action_dispatch.cookies                        : #<ActionDispatch::Cookies::CookieJar:0x00000006564e28>
  * action_dispatch.logger                         : #<ActiveSupport::TaggedLogging:0x0000000318aff8>
  * action_dispatch.parameter_filter               : [:password, /RAW_POST_DATA/, /RAW_POST_DATA/, /RAW_POST_DATA/]
  * action_dispatch.remote_ip                      : 183.60.212.153
  * action_dispatch.request.content_type           : application/x-www-form-urlencoded
  * action_dispatch.request.parameters             : {"action"=>"show", "controller"=>"listings", "id"=>"location-t7-villeurbanne--58"}
  * action_dispatch.request.path_parameters        : {:action=>"show", :controller=>"listings", :id=>"location-t7-villeurbanne--58"}
  * action_dispatch.request.query_parameters       : {}
  * action_dispatch.request.request_parameters     : {}
  * action_dispatch.request.unsigned_session_cookie: {}
  * action_dispatch.request_id                     : 9f8afbc8ff142f91ddbd9cabee3629f3
  * action_dispatch.routes                         : #<ActionDispatch::Routing::RouteSet:0x0000000339f370>
  * action_dispatch.show_detailed_exceptions       : false
  * action_dispatch.show_exceptions                : true
  * rack-cache.allow_reload                        : false
  * rack-cache.allow_revalidate                    : false
  * rack-cache.cache_key                           : Rack::Cache::Key
  * rack-cache.default_ttl                         : 0
  * rack-cache.entitystore                         : rails:/
  * rack-cache.ignore_headers                      : ["Set-Cookie"]
  * rack-cache.metastore                           : rails:/
  * rack-cache.private_headers                     : ["Authorization", "Cookie"]
  * rack-cache.storage                             : #<Rack::Cache::Storage:0x000000039c5768>
  * rack-cache.use_native_ttl                      : false
  * rack-cache.verbose                             : false
  * rack.errors                                    : #<IO:0x000000006592a8>
  * rack.input                                     : #<PhusionPassenger::Utils::RewindableInput:0x0000000655b3a0>
  * rack.multiprocess                              : true
  * rack.multithread                               : false
  * rack.request.cookie_hash                       : {}
  * rack.request.form_hash                         :
  * rack.request.form_input                        : #<PhusionPassenger::Utils::RewindableInput:0x0000000655b3a0>
  * rack.request.form_vars                         : W"qB)
FP   Z 8 &   G\yPuT ed .%mxEA\d*Hg     Clj  U 1]pgtP
      c" LXDHRyp`6llNP lS`V4ycX2        &JO!*p l-Uw }gk ( F J  q:5GJhp]                                                                                                                                                                                                                                                                           zh d }}
  * rack.request.query_hash                        : {}
  * rack.request.query_string                      :
  * rack.run_once                                  : false
  * rack.session                                   : {}
  * rack.session.options                           : {:path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false, :httponly=>true, :defer=>false, :renew=>false, :coder=>#<Rack::Session::Cookie::Base64::Marshal:0x000000034d4ad8>, :id=>nil}
  * rack.url_scheme                                : http
  * rack.version                                   : [1, 0]

Comme vous pouvez le voir, il n'y a pas d'utf-8 invalide dans l'url, mais seulement dans le fichier rack.request.form_vars . J'ai une centaine d'erreurs par jour, et toutes similaires à celle-ci.

J'ai donc essayé de forcer l'utilisation de l'utf-8 en rack.reqest.form_vars avec quelque chose comme ça :

class RackFormVarsSanitizer
  def initialize(app)
    @app = app
  end

  def call(env)
    if env["rack.request.form_vars"] 
      env["rack.request.form_vars"] = env["rack.request.form_vars"].force_encoding('UTF-8')
    end
    @app.call(env)
  end
end

Et je l'appelle dans mon application.rb :

config.middleware.use "RackFormVarsSanitizer"

Cela ne semble pas fonctionner car j'ai déjà des erreurs. Le problème, c'est que je ne peux pas tester en mode développement car je ne sais pas comment paramétrer l'option rack.request.form_vars .

J'ai installé utf8-cleaner mais cela ne résout rien.

Quelqu'un a une idée pour corriger cela ? ou pour le déclencher dans le développement ?

0 votes

Si vous ne pouvez pas régler le rack.request.form_vars Y a-t-il une raison pour laquelle on ne peut pas faire un PORO ? model pour forcer l'encodage. Il suffit de faire une boucle à travers vos paramètres ou quelque chose de similaire à deep_stringify_keys mais en substituant val.force_encodinging('utf-8') pour le to_s ?

1 votes

0 votes

Comme l'a souligné Henrik N, nous avons rencontré le même problème, et la solution qui a fonctionné pour nous a été publiée sur stackoverflow.com/a/24637719/305019

32voto

Henrik N Points 4447

Pour que vous n'ayez pas à reconstituer les commentaires de mon autre réponse, voici ce que je fais maintenant - je n'ai vu aucune erreur pendant 24 heures, donc c'est très prometteur :

Ajouter rack-utf8_sanitizer à votre Gemfile :

gem 'rack-utf8_sanitizer'

et exécuter

bundle

Poner cet intergiciel sur app/middleware/handle_invalid_percent_encoding.rb et renommez la classe HandleInvalidPercentEncoding (parce que ExceptionApp est un peu trop général).

Dans le config bloc de config/application.rb faire :

require "#{Rails.root}/app/middleware/handle_invalid_percent_encoding.rb"

# NOTE: These must be in this order relative to each other.
# HandleInvalidPercentEncoding just raises for encoding errors it doesn't cover,
# so it must run after (= be inserted before) Rack::UTF8Sanitizer.
config.middleware.insert 0, HandleInvalidPercentEncoding
config.middleware.insert 0, Rack::UTF8Sanitizer  # from a gem

Déployer. C'est fait.

( app se trouve être l'emplacement du middleware dans le projet sur lequel je travaille, mais je préférerais probablement lib . Peu importe. L'un ou l'autre devrait fonctionner.)

0 votes

Je vais essayer votre solution aujourd'hui et demain afin de l'accepter. Merci !

0 votes

Plus d'erreur depuis que j'applique votre solution ! Excellent travail !

0 votes

Je viens de l'essayer et, sans surprise, j'ai obtenu une erreur "HandleInvalidPercentEncoding not defined". En regardant le middleware, cette classe particulière n'est pas définie. J'ai pensé que vous vous étiez peut-être appuyé sur les conventions Rails pour les noms de fichiers (MyClass correspond à my_class.rb), mais cela ne fonctionne pas pour moi.

11voto

Sunny Points 1468

Ajoutez cette ligne à votre Gemfile puis exécutez bundle dans votre terminal :

gem "handle_invalid_percent_encoding_requests"

Cette solution est basée sur Réponse d'Henrik transformé en une gemme du moteur Rails .

0 votes

Ceci n'est compatible qu'avec Rails 4.1.4 ?

1 votes

Le gist référencé par Henrik ne dépend pas du tout de Rails. Tout comme rack-utf8-sanitizer. Comment je le sais ? J'ai écrit le premier et je maintiens le second :) Merci à Sunny pour avoir gemifié le code dans le gist.

0 votes

@BF4 Merci pour le code original ! J'ai mis à jour ma réponse pour qu'il soit plus clair qu'il s'agit d'un moteur Rails. Si les gens souhaitent l'utiliser en dehors de Rails, j'accepterai volontiers les demandes de retrait qui suppriment la dépendance.

0voto

Henrik N Points 4447

Il y a un problème dans le dépôt de gemmes avec un lien vers la solution possible de quelqu'un - ils disent que ça marche pour eux mais ils ne sont pas sûrs que ce soit une bonne solution.

Je n'ai pas encore essayé, mais je pense que je le ferai.

0 votes

J'ai installé la gemme avec la demande de pull dans 2 sites web, je verrai demain s'il y a des avantages.

0 votes

Depuis que j'ai déployé ce système, j'ai malheureusement vu un "invalid byte sequence in UTF-8" et un "invalid %-encoding" dus à EasouSpider. J'ai l'impression qu'il y a moins d'erreurs qu'avant, donc peut-être que cela a aidé un peu.

0 votes

Je suis d'accord, j'ai vu quelques erreurs, mais peut-être moins qu'avant. (dix ce jour contre cent auparavant...). C'est vraiment étrange car les erreurs semblent être équivalentes aux autres. Je ne comprends pas pourquoi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X