├── client
├── .htaccess
├── master.pub
├── index.html
├── master.key
└── backdoorkey.php
├── concrete5
├── icon.png
├── README
├── controller.php
└── controllers
│ └── pubkey.php
├── server
├── index.php
├── pubkeys
│ └── master.pub
└── backdoor.php
└── README.md
/client/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | deny from all
3 |
4 |
--------------------------------------------------------------------------------
/concrete5/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jasny/developer-access/HEAD/concrete5/icon.png
--------------------------------------------------------------------------------
/server/index.php:
--------------------------------------------------------------------------------
1 | {$_SESSION['user']}Logout" : "
Guest
";
6 |
--------------------------------------------------------------------------------
/client/master.pub:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9JBriR4yYb5pTN8KHnOFDLv0H
3 | CAHdG+YkhaNtBrX8QsOfWOd8DvML6vsAbrH+juy9wr3dwmrXvi6MvSKMrPwO1Ch1
4 | byUajOObSE4fnXNkttPH3x0PmEqH9hfKGN/vEife1S7zWqy05WC8jYSmhG55ZHbt
5 | 4R6QaGBlmObxdpcZ3wIDAQAB
6 | -----END PUBLIC KEY-----
7 |
--------------------------------------------------------------------------------
/server/pubkeys/master.pub:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9JBriR4yYb5pTN8KHnOFDLv0H
3 | CAHdG+YkhaNtBrX8QsOfWOd8DvML6vsAbrH+juy9wr3dwmrXvi6MvSKMrPwO1Ch1
4 | byUajOObSE4fnXNkttPH3x0PmEqH9hfKGN/vEife1S7zWqy05WC8jYSmhG55ZHbt
5 | 4R6QaGBlmObxdpcZ3wIDAQAB
6 | -----END PUBLIC KEY-----
7 |
--------------------------------------------------------------------------------
/concrete5/README:
--------------------------------------------------------------------------------
1 | == Public Key Authentication ==
2 |
3 | The 'Public Key Authentication' add-on provides access to an application through a
4 | different method that the normal login process. The add-on verifies the authenticity
5 | of the login credentials using a public key. You may know this method from SSH
6 | authentication using DSA public/private keys.
7 |
8 | Place the public keys in 'config/backdoor'.
9 |
10 | Requirements
11 | * This add-on requires the PHP OpenSSL extension.
12 | * This is only the backdoor server. To log in you need a client. An example client
13 | can be downloaded from http://github.com/jasny/backdoor.
14 |
15 | For more information see http://www.jasny.net/articles/a-secure-backdoor-for-php
16 |
17 | MIT licensed - http://www.jasny.net/mit-license/
18 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Backdoor key
6 |
7 |
8 | Backdoor key
9 |
18 |
19 |
--------------------------------------------------------------------------------
/client/master.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQC9JBriR4yYb5pTN8KHnOFDLv0HCAHdG+YkhaNtBrX8QsOfWOd8
3 | DvML6vsAbrH+juy9wr3dwmrXvi6MvSKMrPwO1Ch1byUajOObSE4fnXNkttPH3x0P
4 | mEqH9hfKGN/vEife1S7zWqy05WC8jYSmhG55ZHbt4R6QaGBlmObxdpcZ3wIDAQAB
5 | AoGAOi9eOdgq70X94rXXmSJwxdsxqbUT5bJMdVXLNaIH4Ael6GJQLADpUfRCrRpg
6 | lV/ggmvjPtItThtKdK9GbqJUk68DRLgVwDQmBPUyyuZgRJjZSZS/vS5Fh11tovmI
7 | BZ6cFRIZ/ZW15PieJB8xJRfCHe90IxQTMs1W/yd0ppY/l5kCQQD2DEFj6EMcoBlY
8 | JIlCDaWSjVLQuC1gbJe6oknm00Eqy9Jk0ljj7UVwu6UjJxxFmkQiyqO8iXSHUFG8
9 | 88XRmGudAkEAxMqZGd9LF1natKAu8bLv0tIGnEZo7cqxdHjVzURrsIJJIcl9qAjV
10 | JUuGTQ42nnqKSU2vM601+p0BBcqjfMqYqwJAM1sFfwPolh34jEQ7/fR5PaiPw47d
11 | scWYudTL1gj8DiGyzWZ2NUS9/LS92CN+rCxx0k03dc++6cti9Cxxerj0pQJAOJAX
12 | A2W3+qU8BJDKDXSmFA2EEuT2AesxqE2W/mH9JgW8qXZ1pJetVAPqPTmpn1GPb9Rh
13 | WFQf4MHB6n09EoBU8wJBANFFisj9UlohInH+f7wa16ZYLt/cpI35RFumbpzOJsin
14 | 4nIMq6SxUr5Xg8jxiSSDi9IgqF8KY2y7gqkTV6okebk=
15 | -----END RSA PRIVATE KEY-----
16 |
--------------------------------------------------------------------------------
/concrete5/controller.php:
--------------------------------------------------------------------------------
1 | update(array('cFilename'=>"/pubkey.php"));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/client/backdoorkey.php:
--------------------------------------------------------------------------------
1 | generateLink($_REQUEST['system'], $_REQUEST['user']);
28 |
29 | header("Location: $link");
30 | echo "You are being redirected to {$_REQUEST['system']}.";
31 | exit();
32 | }
33 |
34 | /**
35 | * Output link to target url backdoor hash and signature.
36 | */
37 | public function getLink()
38 | {
39 | echo $this->generateLink($_REQUEST['system'], $_REQUEST['user']);
40 | exit();
41 | }
42 |
43 | /**
44 | * Get link to target url backdoor hash and signature.
45 | *
46 | * @param string $url
47 | * @param string $user
48 | */
49 | public function generateLink($system=null, $user=null)
50 | {
51 | if (!isset($system)) throw new Exception("Unable to get backdoor link: System not specified");
52 | if (!isset($user)) throw new Exception("Unable to get backdoor link: User not specified");
53 |
54 | $timeout = time() + 5;
55 | $hash = "$system|$user|$timeout";
56 |
57 | $signature = null;
58 | $key = openssl_get_privatekey(file_get_contents(dirname(__FILE__) . "/master.key"));
59 | if (!openssl_sign($hash, $signature, $key)) throw new Exception("Failed to sign backdoor hash");
60 |
61 | return str_replace(array('{%system}'), array($system, 'login'), $this->link) . (strpos($this->link, '?') !== false ? '&' : '?') . "system=" . urlencode($system) . "&user=" . urlencode($user) . "&timeout=" . urlencode($timeout) . "&signature=" . urlencode(base64_encode($signature));
62 | }
63 | }
64 |
65 | // Execute controller command
66 | if (realpath($_SERVER["SCRIPT_FILENAME"]) == realpath(__FILE__)) {
67 | $ctl = new BackdoorClient();
68 |
69 | try {
70 | $cmd = !empty($_REQUEST['cmd']) ? $_REQUEST['cmd'] : 'go';
71 | if (!empty($_REQUEST['link'])) $ctl->link = $_REQUEST['link'];
72 | $ctl->$cmd();
73 |
74 | } catch (Exception $e) {
75 | echo $e->getMessage();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/server/backdoor.php:
--------------------------------------------------------------------------------
1 | system) throw new Exception("Unable to use backdoor: Signature is for system '{$_GET['system']}' instead of '{$this->system}'");
29 | if ($_GET['timeout'] < time()) throw new Exception("Unable to use backdoor: Signature has timed out");
30 |
31 | $hash = $_GET['system'] . '|' . $_GET['user'] . '|' . $_GET['timeout'];
32 | $signature = base64_decode($_GET['signature']);
33 |
34 | $dir = dirname(__FILE__) . '/pubkeys';
35 | foreach (scandir($dir) as $file) {
36 | if (!is_file("$dir/$file")) continue;
37 |
38 | $key = openssl_get_publickey(file_get_contents("$dir/$file"));
39 | $ret = openssl_verify($hash, $signature, $key);
40 | if ($ret == 1) {
41 | $this->setUser($_GET['user']);
42 | return;
43 | }
44 | }
45 |
46 | throw new Exception("Unable to use backdoor: Signature could not be verified.");
47 | }
48 |
49 | /**
50 | * Set specified user a logged in user.
51 | *
52 | * @param string $user
53 | */
54 | protected function setUser($user)
55 | {
56 | session_start();
57 | $_SESSION['user'] = $user;
58 | header('Location: ' . dirname($_SERVER['REQUEST_URI']) . '/index.php');
59 | exit();
60 | }
61 | }
62 |
63 | // Determining the system based on HTTP_HOST is not very secure, please set this manually.
64 | $system = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . (dirname($_SERVER['REQUEST_URI']) == '/' ? '' : dirname($_SERVER['REQUEST_URI']));
65 |
66 | // Execute controller command
67 | if (realpath($_SERVER["SCRIPT_FILENAME"]) == realpath(__FILE__)) {
68 | $ctl = new Backdoor();
69 |
70 | try {
71 | $ctl->system = $system;
72 | $ctl->login();
73 | } catch (Exception $e) {
74 | header("HTTP/1.0 400 Bad Request");
75 | echo $e->getMessage();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Developer access in PHP application
2 |
3 | Private/public key authentication in PHP, granting you as developer access to the application.
4 |
5 | CAUTION! Make sure you replace master.key/master.pub with your own private and public key.
6 |
7 | openssl genrsa -out master.key 1024
8 | openssl rsa -in master.key -pubout -out master.pub
9 |
10 | MIT licensed - http://www.jasny.net/MIT.txt
11 |
12 | # A secure backdoor for PHP
13 |
14 | A backdoor provides access to an application bypassing the normal authentication process. There are many ways to do this. Some are more secure than others.
15 |
16 | ## Why do you need a backdoor?
17 |
18 | In a perfect world you could just deliver an application and all would be good. However in the real world there are unforeseen issues which need to be solved. This means that you as a developer will need access to the application. To reproduce the problem, you usually want to run the application logged in as the user that spotted the issue.
19 |
20 | Another use of the backdoor is in a situation where you want to allow a user, that has already been authenticated, to bypassing further authentication. For example if you have a (web hosting) control panel where the user is already logged in, you can allow him to directly access the dashboard of the application without have to enter his password again. This requires a backdoor, since you don’t know his (unencrypted) password.
21 |
22 | ## A very simple solution
23 |
24 | The most simple solution is to use a backdoor password. This password will work for every user. A variation on this, is to have a superuser account, that is allowed to switch to any user on the system.
25 |
26 | This solution is fine if you’re the only developer working on these applications. However in a professional environment this solution won’t do. With this method it is easy to give somebody super privileges, but hard to take them away. This requires changing the backdoor password. Which is a tedious job if you’re managing any serious number of applications.
27 |
28 | ## The secure way
29 |
30 | It is easier if there is a project management system where you and other developers can log into. From within that system, the developer can directly login the customer application as any user. Within that application you can configure on which team each developer is. That limits to which applications the developer has access. More important, simply blocking the user account on the project management system will lock the developer out completely.
31 |
32 | ## Private and public keys
33 |
34 | The best known method for logging into a system, is the use of private/public (DSA) keys with SSH. The SSH client signs the request with the private key. The SSH server has the public key in the authorized_key file. It verifies the credentials using the public keys and grands access on success.
35 |
36 | We can use the same method with PHP using the OpenSSL extension. We’ll let the client (project management system) sign the username and system name (URL) using openssl\_sign. This signature is verified on the server (customer application) using openssl\_verify. To unsure the login URL can’t be reused later, we’ll throw in a 5 second timeout.
37 |
38 | ## Generating the keys
39 |
40 | The keys can be generated on the (*nix) command line, using the ‘openssl’ binary. I’m using RSA keys, but DSA should also work if preferred.
41 |
42 | # Generate private key
43 | openssl genrsa -out master.key 1024
44 | # Generate public key
45 | openssl rsa -in master.key -pubout -out master.pub
46 |
47 | The public key should be copied to the ‘pubkeys’ directory of the server application. Make sure the private key is absolutely private. Anybody who has a copy of that, can use the backdoor.
48 |
--------------------------------------------------------------------------------
/concrete5/controllers/pubkey.php:
--------------------------------------------------------------------------------
1 | error = Loader::helper('validation/error');
21 | }
22 |
23 | /**
24 | * Login through the pubkey.
25 | */
26 | public function do_login()
27 | {
28 | $ip = Loader::helper('validation/ip');
29 |
30 | try {
31 | if (!isset($_GET['system'])) throw new Exception("Unable to use pubkey: System not specified");
32 | if (!isset($_GET['user'])) throw new Exception("Unable to use pubkey: User not specified");
33 | if (!isset($_GET['timeout'])) throw new Exception("Unable to use pubkey: Timeout not specified");
34 | if (!isset($_GET['signature'])) throw new Exception("Unable to use pubkey: Signature not specified");
35 |
36 | if ($_GET['system'] != BASE_URL . DIR_REL) throw new Exception("Unable to use pubkey: Signature is for system '{$_GET['system']}' instead of '{$this->system}'");
37 | if ($_GET['timeout'] < time()) throw new Exception("Unable to use pubkey: Signature has timed out");
38 |
39 | $hash = $_GET['system'] . '|' . $_GET['user'] . '|' . $_GET['timeout'];
40 | $signature = base64_decode($_GET['signature']);
41 |
42 | $dir = DIR_CONFIG_SITE . '/pubkeys';
43 | foreach (scandir($dir) as $file) {
44 | if (!is_file("$dir/$file")) continue;
45 |
46 | $key = openssl_get_publickey(file_get_contents("$dir/$file"));
47 | $ret = openssl_verify($hash, $signature, $key);
48 | if ($ret == 1) {
49 | $loginData = $this->setUser($_GET['user']);
50 | break;
51 | }
52 | }
53 |
54 | if (!isset($loginData)) throw new Exception(t("Unable to use pubkey: Signature could not be verified."));
55 |
56 | } catch(Exception $e) {
57 | $ip->logSignupRequest();
58 | if ($ip->signupRequestThreshholdReached()) {
59 | $ip->createIPBan();
60 | }
61 | if ($_REQUEST['format'] !='JSON') throw $e;
62 |
63 | $loginData['error']=$e->getMessage();
64 | }
65 |
66 | if ($_REQUEST['format']=='JSON') {
67 | $jsonHelper=Loader::helper('json');
68 | echo $jsonHelper->encode($loginData);
69 | die;
70 | }
71 | }
72 |
73 | /**
74 | * Set specified user a logged in user.
75 | *
76 | * @param string $user
77 | */
78 | protected function setUser($user)
79 | {
80 | if (ctype_digit($user)) {
81 | $uid = $user;
82 | } else {
83 | $ui = UserInfo::getByUserName($user);
84 | if (empty($ui)) throw new Exception(sprintf(t("Unable to use pubkey: User '%s' does not exist"), $user));
85 | $uid = $ui->getUserId();
86 | }
87 |
88 | $u = User::loginByUserID($uid);
89 |
90 | $loginData['success']=1;
91 | $loginData['msg']=t('Login Successful');
92 | $loginData['uID'] = intval($u->getUserID());
93 |
94 | $loginData = $this->finishLogin($loginData);
95 | return $loginData;
96 | }
97 |
98 |
99 | /** @ignore **/
100 | public function view()
101 | {
102 | throw new Exception("Incorrect use of pubkey");
103 | }
104 |
105 | /** @ignore */
106 | public function complete_openid()
107 | {
108 | throw new Exception("Incorrect use of pubkey");
109 | }
110 |
111 | /** @ignore */
112 | public function v($hash)
113 | {
114 | throw new Exception("Incorrect use of pubkey");
115 | }
116 |
117 | /** @ignore */
118 | public function change_password($uHash)
119 | {
120 | throw new Exception("Incorrect use of pubkey");
121 | }
122 |
123 | /** @ignore */
124 | public function forgot_password()
125 | {
126 | throw new Exception("Incorrect use of pubkey");
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------