From 1832c8fa33a93a7c035eafde8b96c568d732c3b0 Mon Sep 17 00:00:00 2001 From: Bojan Petkovski Date: Mon, 6 May 2013 12:07:52 +0200 Subject: [PATCH 01/10] ReCaptcha feature --- doc/config.rst | 243 ++++++++------- libraries/Header.class.php | 16 +- .../auth/AuthenticationCookie.class.php | 52 ++++ libraries/plugins/auth/recaptchalib.php | 277 ++++++++++++++++++ 4 files changed, 471 insertions(+), 117 deletions(-) create mode 100644 libraries/plugins/auth/recaptchalib.php diff --git a/doc/config.rst b/doc/config.rst index d9087ca87d..c9823f7bb5 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -62,13 +62,13 @@ Basic settings :default: false Starting with version 2.3.0 phpMyAdmin offers a lot of features to - work with master / foreign – tables (see :config:option:`$cfg['Servers'][$i]['pmadb']`). - + work with master / foreign – tables (see :config:option:`$cfg['Servers'][$i]['pmadb']`). + If you tried to set this up and it does not work for you, have a look on the :guilabel:`Structure` page of one database where you would like to use it. You will find a link that will analyze why those features have been disabled. - + If you do not want to use those features set this variable to ``true`` to stop this message from appearing. @@ -77,8 +77,8 @@ Basic settings :type: boolean :default: false - A warning is displayed on the main page if Suhosin is detected. - + A warning is displayed on the main page if Suhosin is detected. + You can set this parameter to ``true`` to stop this message from appearing. .. config:option:: $cfg['McryptDisableWarning'] @@ -87,8 +87,8 @@ Basic settings :default: false Disable the default warning that is displayed if mcrypt is missing for - cookie authentication. - + cookie authentication. + You can set this parameter to ``true`` to stop this message from appearing. .. config:option:: $cfg['ServerLibraryDifference_DisableWarning'] @@ -97,8 +97,8 @@ Basic settings :default: false A warning is displayed on the main page if there is a difference - between the MySQL library and server version. - + between the MySQL library and server version. + You can set this parameter to ``true`` to stop this message from appearing. .. config:option:: $cfg['ReservedWordDisableWarning'] @@ -109,8 +109,8 @@ Basic settings This warning is displayed on the Structure page of a table if one or more column names match with words which are MySQL reserved. - If you want to turn off this warning, you can set it to ``true`` and - warning will not longer be displayed + If you want to turn off this warning, you can set it to ``true`` and + warning will not longer be displayed .. config:option:: $cfg['TranslationWarningThreshold'] @@ -139,7 +139,7 @@ Server connection settings define all settings, just those you need to change). .. note:: - + The :config:option:`$cfg['Servers']` array starts with $cfg['Servers'][1]. Do not use $cfg['Servers'][0]. If you want more than one server, just copy following section (including $i @@ -168,10 +168,10 @@ Server connection settings :default: ``''`` The port-number of your $i-th MySQL-server. Default is 3306 (leave - blank). - + blank). + .. note:: - + If you use ``localhost`` as the hostname, MySQL ignores this port number and connects with the socket, so if you want to connect to a port different from the default port, use ``127.0.0.1`` or the real hostname @@ -213,9 +213,9 @@ Server connection settings What php MySQL extension to use for the connection. Valid options are: ``mysql`` - The classic MySQL extension. + The classic MySQL extension. - ``mysqli`` + ``mysqli`` The improved MySQL extension. This extension became available with PHP 5.0.0 and is the recommended way to connect to a server running MySQL 4.1.x or newer. @@ -243,7 +243,7 @@ Server connection settings :type: string :default: ``''`` - Permits to use an alternate port to connect to the host that + Permits to use an alternate port to connect to the host that holds the configuration storage. .. _controluser: @@ -261,7 +261,7 @@ Server connection settings relational features (see :config:option:`$cfg['Servers'][$i]['pmadb']`) and, for a MySQL server running with ``--skip-show-database``, to enable a multi-user installation (:term:`HTTP` or cookie - authentication mode). + authentication mode). When using :term:`HTTP` or cookie authentication modes (or 'config' authentication mode since phpMyAdmin @@ -271,7 +271,7 @@ Server connection settings "Timestamp")* tables. This account is used to check what databases the user will see at login. - .. versionchanged:: 2.2.5 + .. versionchanged:: 2.2.5 those were called ``stduser`` and ``stdpass`` .. seealso:: :ref:`setup`, :ref:`authentication_modes` @@ -302,9 +302,9 @@ Server connection settings is in signon example: :file:`examples/signon.php`. There is also alternative example using OpenID - :file:`examples/openid.php` and example for scripts based solution - :file:`examples/signon-script.php`. You need - to configure :config:option:`$cfg['Servers'][$i]['SignonSession']` or - :config:option:`$cfg['Servers'][$i]['SignonScript']` and - :config:option:`$cfg['Servers'][$i]['SignonURL']` to use this authentication + to configure :config:option:`$cfg['Servers'][$i]['SignonSession']` or + :config:option:`$cfg['Servers'][$i]['SignonScript']` and + :config:option:`$cfg['Servers'][$i]['SignonURL']` to use this authentication method. .. seealso:: :ref:`authentication_modes` @@ -381,12 +381,12 @@ Server connection settings An example of using more that one database: .. code-block:: php - + $cfg['Servers'][$i]['only_db'] = array('db1', 'db2'); - .. versionchanged:: 4.0.0 - Previous versions permitted to specify the display order of - the database names via this directive. + .. versionchanged:: 4.0.0 + Previous versions permitted to specify the display order of + the database names via this directive. .. config:option:: $cfg['Servers'][$i]['hide_db'] @@ -431,11 +431,11 @@ Server connection settings :default: ``''`` The name of the database containing the phpMyAdmin configuration - storage. + storage. See the :ref:`linked-tables` section in this document to see the benefits of this feature, and for a quick way of creating this database and the needed - tables. + tables. If you are the only user of this phpMyAdmin installation, you can use your current database to store those special tables; in this case, just put your @@ -480,7 +480,7 @@ Server connection settings * enable you to get a :term:`PDF` schema of your database (also uses the table\_coords table). - The keys can be numeric or character. + The keys can be numeric or character. To allow the usage of this functionality: @@ -490,8 +490,8 @@ Server connection settings where you want to use this feature, click :guilabel:`Structure/Relation view/` and choose foreign columns. - .. note:: - + .. note:: + In the current version, ``master_db`` must be the same as ``foreign_db``. Those columns have been put in future development of the cross-db relations. @@ -532,7 +532,7 @@ Server connection settings showing the relations between your tables. To do this it needs two tables "pdf\_pages" (storing information about the available :term:`PDF` pages) and "table\_coords" (storing coordinates where each table will be placed on - a :term:`PDF` schema output). You must be using the "relation" feature. + a :term:`PDF` schema output). You must be using the "relation" feature. To allow the usage of this functionality: @@ -551,13 +551,13 @@ Server connection settings This part requires a content update! Since release 2.3.0 you can store comments to describe each column for each table. These will then - be shown on the "printview". + be shown on the "printview". Starting with release 2.5.0, comments are consequently used on the table property pages and table browse view, showing up as tool-tips above the column name (properties page) or embedded within the header of table in browse view. They can also be shown in a table dump. Please see the - relevant configuration directives later on. + relevant configuration directives later on. Also new in release 2.5.0 is a MIME- transformation system which is also based on the following table structure. See :ref:`transformations` for @@ -592,7 +592,7 @@ Server connection settings Since release 2.5.0 you can store your :term:`SQL` history, which means all queries you entered manually into the phpMyAdmin interface. If you don't want to use a table-based history, you can use the JavaScript-based - history. + history. Using that, all your history items are deleted when closing the window. Using :config:option:`$cfg['QueryHistoryMax']` you can specify an amount of @@ -600,7 +600,7 @@ Server connection settings to the maximum amount. The query history is only available if JavaScript is enabled in - your browser. + your browser. To allow the usage of this functionality: @@ -623,7 +623,7 @@ Server connection settings Without configuring the storage, you can still access the recently used tables, - but it will disappear after you logout. + but it will disappear after you logout. To allow the usage of this functionality persistently: @@ -641,7 +641,7 @@ Server connection settings things (sorted column :config:option:`$cfg['RememberSorting']`, column order, and column visibility from a database table) for browsing tables. Without configuring the storage, these features still can be used, but the values will - disappear after you logout. + disappear after you logout. To allow the usage of these functionality persistently: @@ -660,7 +660,7 @@ Server connection settings track every :term:`SQL` command which is executed by phpMyAdmin. The mechanism supports logging of data manipulation and data definition statements. After enabling it you can - create versions of tables. + create versions of tables. The creation of a version has two effects: @@ -707,7 +707,7 @@ Server connection settings :default: ``'CREATE TABLE,ALTER TABLE,DROP TABLE,RENAME TABLE,CREATE INDEX,DROP INDEX,INSERT,UPDATE,DELETE,TRUNCATE,REPLACE,CREATE VIEW,ALTER VIEW,DROP VIEW,CREATE DATABASE,ALTER DATABASE,DROP DATABASE'`` Defines the list of statements the auto-creation uses for new - versions. + versions. .. _tracking4: .. config:option:: $cfg['Servers'][$i]['tracking_add_drop_view'] @@ -748,7 +748,7 @@ Server connection settings If you don't allow for storing preferences in :config:option:`$cfg['Servers'][$i]['pmadb']`, users can still personalize phpMyAdmin, but settings will be saved in browser's local storage, or, it - is is unavailable, until the end of session. + is is unavailable, until the end of session. To allow the usage of this functionality: @@ -764,7 +764,7 @@ Server connection settings :default: ``''`` Since release 2.10.0 a Designer interface is available; it permits to - visually manage the relations. + visually manage the relations. To allow the usage of this functionality: @@ -780,7 +780,7 @@ Server connection settings :default: 100 Maximum number of rows saved in - :config:option:`$cfg['Servers'][$i]['table_uiprefs']` table. + :config:option:`$cfg['Servers'][$i]['table_uiprefs']` table. When tables are dropped or renamed, :config:option:`$cfg['Servers'][$i]['table_uiprefs']` may contain invalid data @@ -813,19 +813,19 @@ Server connection settings :default: ``''`` If your rule order is empty, then :term:`IP` - authorization is disabled. + authorization is disabled. If your rule order is set to ``'deny,allow'`` then the system applies all deny rules followed by allow rules. Access is allowed by default. Any client which does not match a Deny command or does match an Allow command will be allowed - access to the server. + access to the server. If your rule order is set to ``'allow,deny'`` then the system applies all allow rules followed by deny rules. Access is denied by default. Any client which does not match an Allow directive or does match a Deny directive will be denied access to the - server. + server. If your rule order is set to ``'explicit'``, authorization is performed in a similar fashion to rule order 'deny,allow', with the @@ -833,7 +833,7 @@ Server connection settings listed in the *allow* rules, and not listed in the *deny* rules. This is the **most** secure means of using Allow/Deny rules, and was available in Apache by specifying allow and deny rules without setting - any order. + any order. Please also see :config:option:`$cfg['TrustedProxies']` for detecting IP address behind proxies. @@ -847,7 +847,7 @@ Server connection settings The general format for the rules is as such: .. code-block:: none - + <'allow' | 'deny'> [from] If you wish to match all users, it is possible to use a ``'%'`` as a @@ -859,7 +859,7 @@ Server connection settings .. code-block:: none - + 'all' -> 0.0.0.0/0 'localhost' -> 127.0.0.1/8 'localnetA' -> SERVER_ADDRESS/8 @@ -872,15 +872,15 @@ Server connection settings ``'explicit'``. For the :term:`IP address` matching - system, the following work: + system, the following work: - * ``xxx.xxx.xxx.xxx`` (an exact :term:`IP address`) - * ``xxx.xxx.xxx.[yyy-zzz]`` (an :term:`IP address` range) - * ``xxx.xxx.xxx.xxx/nn`` (CIDR, Classless Inter-Domain Routing type :term:`IP` addresses) + * ``xxx.xxx.xxx.xxx`` (an exact :term:`IP address`) + * ``xxx.xxx.xxx.[yyy-zzz]`` (an :term:`IP address` range) + * ``xxx.xxx.xxx.xxx/nn`` (CIDR, Classless Inter-Domain Routing type :term:`IP` addresses) - But the following does not work: + But the following does not work: - * ``xxx.xxx.xxx.xx[yyy-zzz]`` (partial :term:`IP` address range) + * ``xxx.xxx.xxx.xx[yyy-zzz]`` (partial :term:`IP` address range) For :term:`IPv6` addresses, the following work: @@ -915,7 +915,7 @@ Server connection settings When using ``false``, it will disable fetching databases from the server, only databases in :config:option:`$cfg['Servers'][$i]['only_db']` will be displayed. - + Examples: * ``'SHOW DATABASES'`` @@ -951,7 +951,7 @@ Server connection settings Name of session which will be used for signon authentication method. You should use something different than ``phpMyAdmin``, because this - is session which phpMyAdmin uses internally. Takes effect only if + is session which phpMyAdmin uses internally. Takes effect only if :config:option:`$cfg['Servers'][$i]['SignonScript']` is not configured. .. config:option:: $cfg['Servers'][$i]['SignonURL'] @@ -980,9 +980,9 @@ Server connection settings Enables caching of ``TABLE STATUS`` outputs for specific databases on this server (in some cases ``TABLE STATUS`` can be very slow, so you may want to cache it). APC is used (if the PHP extension is available, - if not, this setting is ignored silently). You have to provide - :config:option:`$cfg['Servers'][$i]['StatusCacheLifetime']`. - + if not, this setting is ignored silently). You have to provide + :config:option:`$cfg['Servers'][$i]['StatusCacheLifetime']`. + Takes effect only if :config:option:`$cfg['Servers'][$i]['DisableIS']` is ``true``. @@ -991,9 +991,18 @@ Server connection settings :type: integer :default: 0 - Lifetime in seconds of the ``TABLE STATUS`` cache if + Lifetime in seconds of the ``TABLE STATUS`` cache if :config:option:`$cfg['Servers'][$i]['StatusCacheDatabases']` is used. +.. config:option:: $cfg['Servers'][$i]['captchaLogin'] + + :type: string + :default: ``false`` + + Enables reCaptcha service for the specific server login screen. Works only + with cookie authentication type. It must have $cfg['captchaLoginPublicKey'] + and $cfg['captchaLoginPrivateKey'] specified which are the reCaptcha keys. + Generic settings ---------------- @@ -1005,8 +1014,8 @@ Generic settings If you have more than one server configured, you can set :config:option:`$cfg['ServerDefault']` to any one of them to autoconnect to that server when phpMyAdmin is started, or set it to 0 to be given a list - of servers without logging in. - + of servers without logging in. + If you have only one server configured, :config:option:`$cfg['ServerDefault']` MUST be set to that server. @@ -1111,12 +1120,12 @@ Generic settings :default: ``'0'`` Set the number of bytes a script is allowed to allocate. If set to - zero, no limit is imposed. - + zero, no limit is imposed. + This setting is used while importing/exporting dump files and at some other places in phpMyAdmin so you definitely don't want to put here a too low - value. It has no effect when PHP is running in safe mode. - + value. It has no effect when PHP is running in safe mode. + You can also use any string as in :file:`php.ini`, eg. '16M'. Ensure you don't omit the suffix (16 means 16 bytes!) @@ -1162,10 +1171,10 @@ Generic settings Defines whether normal users (non-administrator) are allowed to delete their own database or not. If set as false, the link :guilabel:`Drop Database` will not be shown, and even a ``DROP DATABASE mydatabase`` will - be rejected. Quite practical for :term:`ISP` 's with many customers. + be rejected. Quite practical for :term:`ISP` 's with many customers. + + .. note:: - .. note:: - This limitation of :term:`SQL` queries is not as strict as when using MySQL privileges. This is due to nature of :term:`SQL` queries which might be quite @@ -1207,7 +1216,7 @@ Cookie authentication options password. If you are using the "cookie" auth\_type, enter here a random passphrase of your choice. It will be used internally by the blowfish algorithm: you won’t be prompted for this passphrase. There - is no maximum length for this secret. + is no maximum length for this secret. .. versionchanged:: 3.1.0 Since version 3.1.0 phpMyAdmin can generate this on the fly, but it @@ -1221,8 +1230,8 @@ Cookie authentication options :default: true Define whether the previous login should be recalled or not in cookie - authentication mode. - + authentication mode. + This is automatically disabled if you do not have configured :config:option:`$cfg['blowfish_secret']`. @@ -1267,11 +1276,27 @@ Cookie authentication options authentication. .. note:: - + Please use this carefully, as this may allow users access to MySQL servers behind the firewall where your :term:`HTTP` server is placed. +.. config:option:: $cfg['captchaLoginPublicKey'] + + :type: string + :default: ``''`` + + The public key for the reCaptcha service that can be obtain from + http://www.google.com/recaptcha. + +.. config:option:: $cfg['captchaLoginPrivateKey'] + + :type: string + :default: ``''`` + + The private key for the reCaptcha service that can be obtain from + http://www.google.com/recaptcha. + Navigation panel setup ---------------------- @@ -1361,8 +1386,8 @@ Navigation panel setup Defines the minimum number of items (tables, views, routines and events) to display a JavaScript filter box above the list of items in - the navigation tree. - + the navigation tree. + To disable the filter completely some high number can be used (e.g. 9999) .. config:option:: $cfg['NavigationTreeDisplayDbFilterMinimum'] @@ -1372,14 +1397,14 @@ Navigation panel setup Defines the minimum number of databases to display a JavaScript filter box above the list of databases in the navigation tree. - + To disable the filter completely some high number can be used (e.g. 9999) .. config:option:: $cfg['NavigationDisplayServers'] :type: boolean - :default: true + :default: true Defines whether or not to display a server choice at the top of the navigation panel. @@ -1425,7 +1450,7 @@ Main panel :default: true Defines whether to display detailed server information on main page. - You can additionally hide more information by using + You can additionally hide more information by using :config:option:`$cfg['Servers'][$i]['verbose']`. .. config:option:: $cfg['ShowPhpInfo'] @@ -1446,8 +1471,8 @@ Main panel Defines whether to display the :guilabel:`PHP information` and :guilabel:`Change password` links and form for creating database or not at the starting main (right) frame. This setting does not check MySQL commands - entered directly. - + entered directly. + Please note that to block the usage of ``phpinfo()`` in scripts, you have to put this in your :file:`php.ini`: @@ -1727,10 +1752,10 @@ Tabs display settings :default: ``'db_structure.php'`` Defines the tab displayed by default on database view. Possible - values: - + values: + * ``db_structure.php`` - * ``db_sql.php`` + * ``db_sql.php`` * ``db_search.php``. .. config:option:: $cfg['DefaultTabTable'] @@ -1743,7 +1768,7 @@ Tabs display settings * ``tbl_structure.php`` * ``tbl_sql.php`` * ``tbl_select.php`` - * ``tbl_change.php`` + * ``tbl_change.php`` * ``sql.php`` Documentation @@ -1757,7 +1782,7 @@ Documentation If set to an :term:`URL` which points to the MySQL documentation (type depends on :config:option:`$cfg['MySQLManualType']`), appropriate help links are - generated. + generated. See `MySQL Documentation page `_ for more information about MySQL manuals and their types. @@ -1875,17 +1900,17 @@ Web server settings :type: array :default: array() - Lists proxies and HTTP headers which are trusted for + Lists proxies and HTTP headers which are trusted for :config:option:`$cfg['Servers'][$i]['AllowDeny']['order']`. This list is by default empty, you need to fill in some trusted proxy servers if you - want to use rules for IP addresses behind proxy. + want to use rules for IP addresses behind proxy. The following example specifies that phpMyAdmin should trust a HTTP\_X\_FORWARDED\_FOR (``X -Forwarded-For``) header coming from the proxy 1.2.3.4: .. code-block:: php - + $cfg['TrustedProxies'] = array('1.2.3.4' => 'HTTP_X_FORWARDED_FOR'); The :config:option:`$cfg['Servers'][$i]['AllowDeny']['rules']` directive uses the @@ -2272,11 +2297,11 @@ Web server upload/save/import directories The name of the directory where :term:`SQL` files have been uploaded by other means than phpMyAdmin (for example, ftp). Those files are available under a drop-down box when you click the database or table name, then the - Import tab. + Import tab. If you want different directory for each user, %u will be replaced with - username. + username. Please note that the file names must have the suffix ".sql" (or ".sql.bz2" or ".sql.gz" if support for compressed formats is @@ -2287,7 +2312,7 @@ Web server upload/save/import directories uploads are disabled in PHP. .. note:: - + If PHP is running in safe mode, this directory must be owned by the same user as the owner of the phpMyAdmin scripts. See also :ref:`faq1_16` for alternatives. @@ -2297,16 +2322,16 @@ Web server upload/save/import directories :type: string :default: ``''`` - The name of the directory where dumps can be saved. + The name of the directory where dumps can be saved. If you want different directory for each user, %u will be replaced with username. Please note that the directory must exist and has to be writable for - the user running webserver. + the user running webserver. + + .. note:: - .. note:: - If PHP is running in safe mode, this directory must be owned by the same user as the owner of the phpMyAdmin scripts. @@ -2315,11 +2340,11 @@ Web server upload/save/import directories :type: string :default: ``''`` - The name of the directory where temporary files can be stored. + The name of the directory where temporary files can be stored. This is needed for importing ESRI Shapefiles, see :ref:`faq6_30` and to work around limitations of ``open_basedir`` for uploaded files, see - :ref:`faq1_11`. + :ref:`faq1_11`. If the directory where phpMyAdmin is installed is subject to an ``open_basedir`` restriction, you need to create a @@ -2336,7 +2361,7 @@ Web server upload/save/import directories .. code-block:: sh - + chown www-data:www-data tmp chmod 700 tmp @@ -2405,25 +2430,25 @@ Various display setting icon is always displayed in the navigation panel. If JavaScript is enabled in your browser, a click on this opens a distinct query window, which is a direct interface to enter :term:`SQL` queries. Otherwise, the right panel - changes to display a query box. + changes to display a query box. The size of this query window can be customized with :config:option:`$cfg['QueryWindowWidth']` and :config:option:`$cfg['QueryWindowHeight']` - both integers for the size in pixels. Note that normally, those parameters will be modified in - :file:`layout.inc.php`` for the theme you are using. + :file:`layout.inc.php`` for the theme you are using. If :config:option:`$cfg['EditInWindow']` is set to true, a click on [Edit] from the results page (in the :guilabel:`Showing Rows` section) opens the query window and puts the current query inside it. If set to false, clicking on the link puts the :term:`SQL` query - in the right panel's query box. + in the right panel's query box. If :config:option:`$cfg['QueryHistoryDB']` is set to ``true``, all your Queries are logged to a table, which has to be created by you (see :config:option:`$cfg['Servers'][$i]['history']`). If set to false, all your queries will be appended to the form, but only as long as your window is - opened they remain saved. + opened they remain saved. When using the JavaScript based query window, it will always get updated when you click on a new table/db to browse and will focus if you click on @@ -2435,11 +2460,11 @@ Various display setting you first have to look in. The checkbox will get automatically checked whenever you change the contents of the textarea. Please uncheck the button whenever you definitely want the query window to get updated even though - you have made alterations. + you have made alterations. If :config:option:`$cfg['QueryHistoryDB']` is set to ``true`` you can specify the amount of saved history items using - :config:option:`$cfg['QueryHistoryMax']`. + :config:option:`$cfg['QueryHistoryMax']`. The query window also has a custom tabbed look to group the features. Using the variable :config:option:`$cfg['QueryWindowDefTab']` you can @@ -2614,7 +2639,7 @@ SQL parser settings For the pretty-printing of :term:`SQL` queries, under some cases the part of a query inside a bracket is indented. By changing :config:option:`$cfg['SQP']['fmtInd']` you can change the amount - of this indent. + of this indent. Related in purpose is :config:option:`$cfg['SQP']['fmtIndUnit']` which specifies the units of the indent amount that you specified. This is used @@ -2640,7 +2665,7 @@ SQL parser settings If you specify an empty string for the color of a class, it is ignored in creating the stylesheet. You should not alter the class names, only the colour strings. - + **Class name key:** comment @@ -2778,4 +2803,4 @@ Developer :default: false Whether to gather errors from PHP or not. - + diff --git a/libraries/Header.class.php b/libraries/Header.class.php index b50b246bfa..ffc5e9b293 100644 --- a/libraries/Header.class.php +++ b/libraries/Header.class.php @@ -448,9 +448,9 @@ class PMA_Header $GLOBALS['now'] = gmdate('D, d M Y H:i:s') . ' GMT'; if (! defined('TESTSUITE')) { header( - "X-Content-Security-Policy: default-src 'self';" + "X-Content-Security-Policy: default-src 'self' http://www.google.com/;" . "options inline-script eval-script;" - . "img-src 'self' data:" + . "img-src 'self' http://www.google.com/ data:" . ($https ? "" : $mapTilesUrls) . ";" ); @@ -458,18 +458,18 @@ class PMA_Header && PMA_USR_BROWSER_VER < '6.0.0' ) { header( - "X-WebKit-CSP: allow 'self';" + "X-WebKit-CSP: allow 'self' http://www.google.com/;" . "options inline-script eval-script;" - . "img-src 'self' data:" + . "img-src 'self' http://www.google.com/ data:" . ($https ? "" : $mapTilesUrls) . ";" ); } else { header( - "X-WebKit-CSP: default-src 'self';" - . "script-src 'self' 'unsafe-inline' 'unsafe-eval';" - . "style-src 'self' 'unsafe-inline';" - . "img-src 'self' data:" + "X-WebKit-CSP: default-src 'self' http://www.google.com/;" + . "script-src 'self' 'unsafe-inline' 'unsafe-eval' http://www.google.com/;" + . "style-src 'self' 'unsafe-inline' http://www.google.com/;" + . "img-src 'self' http://www.google.com/ data:" . ($https ? "" : $mapTilesUrls) . ";" ); diff --git a/libraries/plugins/auth/AuthenticationCookie.class.php b/libraries/plugins/auth/AuthenticationCookie.class.php index aeaae83bc5..9f83df09ba 100644 --- a/libraries/plugins/auth/AuthenticationCookie.class.php +++ b/libraries/plugins/auth/AuthenticationCookie.class.php @@ -239,6 +239,26 @@ class AuthenticationCookie extends AuthenticationPlugin . $GLOBALS['server'] . '" />'; } // end if (server choice) + // Add captcha input field if $cfg['Servers'][$i]['captchaLogin'] is set to TRUE. + if ( !empty($GLOBALS['cfg']['captchaLoginPrivateKey']) + && !empty($GLOBALS['cfg']['captchaLoginPublicKey']) + && isset($GLOBALS['cfg']['Servers'][$GLOBALS['url_params']['server']]['captchaLogin']) + && $GLOBALS['cfg']['Servers'][$GLOBALS['url_params']['server']]['captchaLogin'] + ) { + // If enabled show captcha to the user on the login screen. + echo ' + '; + } + echo '
'; @@ -329,6 +349,38 @@ class AuthenticationCookie extends AuthenticationPlugin return false; } + // Verify Captcha if it is required. + if ( !empty($GLOBALS['cfg']['captchaLoginPrivateKey']) + && !empty($GLOBALS['cfg']['captchaLoginPublicKey']) + && isset($GLOBALS['cfg']['Servers'][$GLOBALS['url_params']['server']]['captchaLogin']) + && $GLOBALS['cfg']['Servers'][$GLOBALS['url_params']['server']]['captchaLogin'] + ) { + if ( !empty($_POST["recaptcha_challenge_field"]) + && !empty($_POST["recaptcha_response_field"]) + ) { + require_once('libraries/plugins/auth/recaptchalib.php'); + + // Use private key to verify captcha status. + $resp = recaptcha_check_answer ( + $GLOBALS['cfg']['captchaLoginPrivateKey'], + $_SERVER["REMOTE_ADDR"], + $_POST["recaptcha_challenge_field"], + $_POST["recaptcha_response_field"] + ); + + // Check if the captcha entered is valid, if not stop the login. + if ( !$resp->is_valid ) { + return false; + $_SESSION['last_valid_captcha'] = false; + } else { + $_SESSION['last_valid_captcha'] = true; + } + } else { + if ( !isset($_SESSION['last_valid_captcha']) || !$_SESSION['last_valid_captcha'] ) + return false; + } + } + if (! empty($_REQUEST['old_usr'])) { // The user wants to be logged out // -> delete his choices that were stored in session diff --git a/libraries/plugins/auth/recaptchalib.php b/libraries/plugins/auth/recaptchalib.php new file mode 100644 index 0000000000..32c4f4d758 --- /dev/null +++ b/libraries/plugins/auth/recaptchalib.php @@ -0,0 +1,277 @@ + $value ) + $req .= $key . '=' . urlencode( stripslashes($value) ) . '&'; + + // Cut the last '&' + $req=substr($req,0,strlen($req)-1); + return $req; +} + + + +/** + * Submits an HTTP POST to a reCAPTCHA server + * @param string $host + * @param string $path + * @param array $data + * @param int port + * @return array response + */ +function _recaptcha_http_post($host, $path, $data, $port = 80) { + + $req = _recaptcha_qsencode ($data); + + $http_request = "POST $path HTTP/1.0\r\n"; + $http_request .= "Host: $host\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; + $http_request .= "Content-Length: " . strlen($req) . "\r\n"; + $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; + $http_request .= "\r\n"; + $http_request .= $req; + + $response = ''; + if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { + die ('Could not open socket'); + } + + fwrite($fs, $http_request); + + while ( !feof($fs) ) + $response .= fgets($fs, 1160); // One TCP-IP packet + fclose($fs); + $response = explode("\r\n\r\n", $response, 2); + + return $response; +} + + + +/** + * Gets the challenge HTML (javascript and non-javascript version). + * This is called from the browser, and the resulting reCAPTCHA HTML widget + * is embedded within the HTML form it was called from. + * @param string $pubkey A public key for reCAPTCHA + * @param string $error The error given by reCAPTCHA (optional, default is null) + * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false) + + * @return string - The HTML to be embedded in the user's form. + */ +function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) +{ + if ($pubkey == null || $pubkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($use_ssl) { + $server = RECAPTCHA_API_SECURE_SERVER; + } else { + $server = RECAPTCHA_API_SERVER; + } + + $errorpart = ""; + if ($error) { + $errorpart = "&error=" . $error; + } + return ' + + '; +} + + + + +/** + * A ReCaptchaResponse is returned from recaptcha_check_answer() + */ +class ReCaptchaResponse { + var $is_valid; + var $error; +} + + +/** + * Calls an HTTP POST function to verify if the user's guess was correct + * @param string $privkey + * @param string $remoteip + * @param string $challenge + * @param string $response + * @param array $extra_params an array of extra variables to post to the server + * @return ReCaptchaResponse + */ +function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) +{ + if ($privkey == null || $privkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($remoteip == null || $remoteip == '') { + die ("For security reasons, you must pass the remote ip to reCAPTCHA"); + } + + + + //discard spam submissions + if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { + $recaptcha_response = new ReCaptchaResponse(); + $recaptcha_response->is_valid = false; + $recaptcha_response->error = 'incorrect-captcha-sol'; + return $recaptcha_response; + } + + $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", + array ( + 'privatekey' => $privkey, + 'remoteip' => $remoteip, + 'challenge' => $challenge, + 'response' => $response + ) + $extra_params + ); + + $answers = explode ("\n", $response [1]); + $recaptcha_response = new ReCaptchaResponse(); + + if (trim ($answers [0]) == 'true') { + $recaptcha_response->is_valid = true; + } + else { + $recaptcha_response->is_valid = false; + $recaptcha_response->error = $answers [1]; + } + return $recaptcha_response; + +} + +/** + * gets a URL where the user can sign up for reCAPTCHA. If your application + * has a configuration page where you enter a key, you should provide a link + * using this function. + * @param string $domain The domain where the page is hosted + * @param string $appname The name of your application + */ +function recaptcha_get_signup_url ($domain = null, $appname = null) { + return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname)); +} + +function _recaptcha_aes_pad($val) { + $block_size = 16; + $numpad = $block_size - (strlen ($val) % $block_size); + return str_pad($val, strlen ($val) + $numpad, chr($numpad)); +} + +/* Mailhide related code */ + +function _recaptcha_aes_encrypt($val,$ky) { + if (! function_exists ("mcrypt_encrypt")) { + die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed."); + } + $mode=MCRYPT_MODE_CBC; + $enc=MCRYPT_RIJNDAEL_128; + $val=_recaptcha_aes_pad($val); + return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + + +function _recaptcha_mailhide_urlbase64 ($x) { + return strtr(base64_encode ($x), '+/', '-_'); +} + +/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */ +function recaptcha_mailhide_url($pubkey, $privkey, $email) { + if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) { + die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " . + "you can do so at http://www.google.com/recaptcha/mailhide/apikey"); + } + + + $ky = pack('H*', $privkey); + $cryptmail = _recaptcha_aes_encrypt ($email, $ky); + + return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail); +} + +/** + * gets the parts of the email to expose to the user. + * eg, given johndoe@example,com return ["john", "example.com"]. + * the email is then displayed as john...@example.com + */ +function _recaptcha_mailhide_email_parts ($email) { + $arr = preg_split("/@/", $email ); + + if (strlen ($arr[0]) <= 4) { + $arr[0] = substr ($arr[0], 0, 1); + } else if (strlen ($arr[0]) <= 6) { + $arr[0] = substr ($arr[0], 0, 3); + } else { + $arr[0] = substr ($arr[0], 0, 4); + } + return $arr; +} + +/** + * Gets html to display an email address given a public an private key. + * to get a key, go to: + * + * http://www.google.com/recaptcha/mailhide/apikey + */ +function recaptcha_mailhide_html($pubkey, $privkey, $email) { + $emailparts = _recaptcha_mailhide_email_parts ($email); + $url = recaptcha_mailhide_url ($pubkey, $privkey, $email); + + return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); + +} + + +?> From ab55c635e62dbc074673e06f56c008f180883884 Mon Sep 17 00:00:00 2001 From: Bojan Petkovski Date: Fri, 10 May 2013 15:58:42 +0200 Subject: [PATCH 02/10] ReCaptcha config from setup script Added config setting to be available from the ./setup script. Captcha public and private keys are added in the Features->Security tab, the captchaLogin is available in the New Server tab where you can enable or disable reCaptcha for that server. --- libraries/config.default.php | 20 ++++++++++++++++++++ libraries/config/messages.inc.php | 6 ++++++ libraries/config/setup.forms.php | 7 +++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libraries/config.default.php b/libraries/config.default.php index 23c20e22c2..fd7111377e 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -265,6 +265,13 @@ $cfg['Servers'][$i]['LogoutURL'] = ''; */ $cfg['Servers'][$i]['nopassword'] = false; +/** + * Whether to enable reCaptcha service on the login screen. + * + * @global boolean $cfg['Servers'][$i]['recaptcha'] + */ +$cfg['Servers'][$i]['captchaLogin'] = false; + /** * If set to a db-name, only this db is displayed in navigation panel * It may also be an array of db-names @@ -749,6 +756,19 @@ $cfg['IgnoreMultiSubmitErrors'] = false; */ $cfg['AllowArbitraryServer'] = false; +/** + * if reCaptcha is enabled it needs public key to connect with the service + * + * @global string $cfg['captchaLoginPublicKey'] + */ +$cfg['captchaLoginPublicKey'] = ''; + +/** + * if reCaptcha is enabled it needs private key to connect with the service + * + * @global string $cfg['captchaLoginPrivateKey'] + */ +$cfg['captchaLoginPrivateKey'] = ''; /******************************************************************************* * Error handler configuration diff --git a/libraries/config/messages.inc.php b/libraries/config/messages.inc.php index edc5b56de4..89635adb59 100644 --- a/libraries/config/messages.inc.php +++ b/libraries/config/messages.inc.php @@ -407,6 +407,8 @@ $strConfigServers_MaxTableUiprefs_desc = __('Limits number of table preferences $strConfigServers_MaxTableUiprefs_name = __('Maximal number of table preferences to store'); $strConfigServers_nopassword_desc = __('Try to connect without password'); $strConfigServers_nopassword_name = __('Connect without password'); +$strConfigServers_captchaLogin_desc = __('Check if you want tp use reCaptcha on the login screen'); +$strConfigServers_captchaLogin_name = __('Enable reCaptcha'); $strConfigServers_only_db_desc = __('You can use MySQL wildcard characters (% and _), escape them if you want to use their literal instances, i.e. use [kbd]\'my\_db\'[/kbd] and not [kbd]\'my_db\'[/kbd].'); $strConfigServers_only_db_name = __('Show only listed databases'); $strConfigServers_password_desc = __('Leave empty if not using config auth'); @@ -523,5 +525,9 @@ $strConfigVersionCheck_desc = __('Enables check for latest version on main phpMy $strConfigVersionCheck_name = __('Version check'); $strConfigZipDump_desc = __('Enable [a@http://en.wikipedia.org/wiki/ZIP_(file_format)]ZIP[/a] compression for import and export operations'); $strConfigZipDump_name = __('ZIP'); +$strConfigcaptchaLoginPublicKey_desc = __('Enter your public key for your domain reCaptcha service'); +$strConfigcaptchaLoginPublicKey_name = __('Public key for reCaptcha'); +$strConfigcaptchaLoginPrivateKey_desc = __('Enter your private key for your domain reCaptcha service'); +$strConfigcaptchaLoginPrivateKey_name = __('Private key for reCaptcha'); ?> diff --git a/libraries/config/setup.forms.php b/libraries/config/setup.forms.php index 17378b0b92..ccacc3a435 100644 --- a/libraries/config/setup.forms.php +++ b/libraries/config/setup.forms.php @@ -35,7 +35,8 @@ $forms['Servers']['Server'] = array('Servers' => array(1 => array( 'connect_type', 'extension', 'compress', - 'nopassword'))); + 'nopassword', + 'captchaLogin'))); $forms['Servers']['Server_auth'] = array('Servers' => array(1 => array( 'auth_type', ':group:' . __('Config authentication'), @@ -108,7 +109,9 @@ $forms['Features']['Security'] = array( 'LoginCookieRecall', 'LoginCookieValidity', 'LoginCookieStore', - 'LoginCookieDeleteAll'); + 'LoginCookieDeleteAll', + 'captchaLoginPublicKey', + 'captchaLoginPrivateKey'); $forms['Features']['Page_titles'] = array( 'TitleDefault', 'TitleTable', From e9f5a30ae564cfd8301bda6677c5a5406103210b Mon Sep 17 00:00:00 2001 From: Bojan Petkovski Date: Sat, 11 May 2013 17:05:25 +0200 Subject: [PATCH 03/10] ReCaptcha https and CSP bug fixes --- libraries/Header.class.php | 16 ++++++++-------- .../plugins/auth/AuthenticationCookie.class.php | 11 ++++++++--- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libraries/Header.class.php b/libraries/Header.class.php index ffc5e9b293..7d2aab2e47 100644 --- a/libraries/Header.class.php +++ b/libraries/Header.class.php @@ -448,9 +448,9 @@ class PMA_Header $GLOBALS['now'] = gmdate('D, d M Y H:i:s') . ' GMT'; if (! defined('TESTSUITE')) { header( - "X-Content-Security-Policy: default-src 'self' http://www.google.com/;" + "X-Content-Security-Policy: default-src 'self' https://www.google.com;" . "options inline-script eval-script;" - . "img-src 'self' http://www.google.com/ data:" + . "img-src 'self' https://www.google.com data:" . ($https ? "" : $mapTilesUrls) . ";" ); @@ -458,18 +458,18 @@ class PMA_Header && PMA_USR_BROWSER_VER < '6.0.0' ) { header( - "X-WebKit-CSP: allow 'self' http://www.google.com/;" + "X-WebKit-CSP: allow 'self' https://www.google.com;" . "options inline-script eval-script;" - . "img-src 'self' http://www.google.com/ data:" + . "img-src 'self' https://www.google.com data:" . ($https ? "" : $mapTilesUrls) . ";" ); } else { header( - "X-WebKit-CSP: default-src 'self' http://www.google.com/;" - . "script-src 'self' 'unsafe-inline' 'unsafe-eval' http://www.google.com/;" - . "style-src 'self' 'unsafe-inline' http://www.google.com/;" - . "img-src 'self' http://www.google.com/ data:" + "X-WebKit-CSP: default-src 'self' https://www.google.com;" + . "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com;" + . "style-src 'self' 'unsafe-inline' https://www.google.com;" + . "img-src 'self' https://www.google.com data:" . ($https ? "" : $mapTilesUrls) . ";" ); diff --git a/libraries/plugins/auth/AuthenticationCookie.class.php b/libraries/plugins/auth/AuthenticationCookie.class.php index 9f83df09ba..4996c5b877 100644 --- a/libraries/plugins/auth/AuthenticationCookie.class.php +++ b/libraries/plugins/auth/AuthenticationCookie.class.php @@ -247,16 +247,21 @@ class AuthenticationCookie extends AuthenticationPlugin ) { // If enabled show captcha to the user on the login screen. echo ' '; + + '; } echo '
From 4213c3c2852f14814d5a683ae3916e485296e13a Mon Sep 17 00:00:00 2001 From: Bojan Petkovski Date: Sun, 12 May 2013 13:33:09 +0200 Subject: [PATCH 04/10] ReCaptcha error message --- libraries/plugins/auth/AuthenticationCookie.class.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/plugins/auth/AuthenticationCookie.class.php b/libraries/plugins/auth/AuthenticationCookie.class.php index 4996c5b877..76947dd2d3 100644 --- a/libraries/plugins/auth/AuthenticationCookie.class.php +++ b/libraries/plugins/auth/AuthenticationCookie.class.php @@ -329,6 +329,8 @@ class AuthenticationCookie extends AuthenticationPlugin */ public function authCheck() { + global $conn_error; + // Initialization /** * @global $GLOBALS['pma_auth_server'] the user provided server to @@ -375,8 +377,9 @@ class AuthenticationCookie extends AuthenticationPlugin // Check if the captcha entered is valid, if not stop the login. if ( !$resp->is_valid ) { - return false; + $conn_error = __('Entered captcha is wrong, try again!'); $_SESSION['last_valid_captcha'] = false; + return false; } else { $_SESSION['last_valid_captcha'] = true; } From 95f5df348d8f7693c2171992e44ced1cc00d6d71 Mon Sep 17 00:00:00 2001 From: Bojan Petkovski Date: Mon, 20 May 2013 22:43:07 +0200 Subject: [PATCH 05/10] Recaptcha bug fixes Error message for empty captcha "Please enter correct captcha!" and if the user enters correct captcha but wrong login data it won't be required to enter it next time. --- .../auth/AuthenticationCookie.class.php | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/auth/AuthenticationCookie.class.php b/libraries/plugins/auth/AuthenticationCookie.class.php index 76947dd2d3..b107969c8f 100644 --- a/libraries/plugins/auth/AuthenticationCookie.class.php +++ b/libraries/plugins/auth/AuthenticationCookie.class.php @@ -239,11 +239,20 @@ class AuthenticationCookie extends AuthenticationPlugin . $GLOBALS['server'] . '" />'; } // end if (server choice) + // We already have one correct captcha. + $skip = false; + if ( isset($_SESSION['last_valid_captcha']) + && $_SESSION['last_valid_captcha'] + ) { + $skip = true; + } + // Add captcha input field if $cfg['Servers'][$i]['captchaLogin'] is set to TRUE. if ( !empty($GLOBALS['cfg']['captchaLoginPrivateKey']) && !empty($GLOBALS['cfg']['captchaLoginPublicKey']) && isset($GLOBALS['cfg']['Servers'][$GLOBALS['url_params']['server']]['captchaLogin']) && $GLOBALS['cfg']['Servers'][$GLOBALS['url_params']['server']]['captchaLogin'] + && !$skip ) { // If enabled show captcha to the user on the login screen. echo '