Plurk掛了,然後吐出資料庫的連線位址,帳號還有密碼…

台灣時間2011年2月15號晚上11點30分左右,突然間Plurk不能連線了,結果正在上面搜尋資料的話,Plurk Python吐出來的錯誤訊息還有Traceback:

Traceback (most recent call last):
File "/home/plurk/plurk/git_trunk/ext/parts/web/wsgiserver/__init__.py", line 1183, in communicate
req.respond()
File "/home/plurk/plurk/git_trunk/ext/parts/web/wsgiserver/__init__.py", line 553, in respond
self._respond()
File "/home/plurk/plurk/git_trunk/ext/parts/web/wsgiserver/__init__.py", line 565, in _respond
response = self.wsgi_app(self.environ, self.start_response)
File "/home/plurk/plurk/git_trunk/ext/werkzeug/utils.py", line 859, in __call__
return self.app(environ, start_response)
File "/home/plurk/plurk/git_trunk/ext/parts/web/web.py", line 295, in dispatch_request
rv = handle_error()
File "/home/plurk/plurk/git_trunk/ext/parts/web/web.py", line 358, in handle_error
result = handler(e)
File "plurk/web/error_handler.py", line 65, in error_handler
trace_back=t_b)
File "plurk/templates.py", line 143, in renderPlurkTemplate
html = PlurkTemplates().addDynamicData(html, ses_user, page_user)
File "plurk/templates.py", line 149, in addDynamicData
'session_user': users.exposeSessionUser(ses_user),
File "plurk/users.py", line 321, in exposeSessionUser
session_user['notifications_count'] = Notifications().getCount(user.id)
File "/home/plurk/plurk/git_trunk/ext/parts/cache/__init__.py", line 125, in proxy
value = f(*args, **kwargs)
File "plurk/models/notifications.py", line 112, in getCount
where='status in (0, -2)')
File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 236, in selectCount
res = self.select(table, cols="COUNT(%s)" % column, as_one=True, **kw)
File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 120, in select
with self.cursor(sql) as cursor:
File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 54, in cursor
con = self.connections.getConnection(host)
File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 578, in getConnection
cloned_info = dict(dbinfo)
Exception: Could not create a connection on servers [{'charset': 'utf8',
'compress': False,
'exception': 'Traceback (most recent call last):\n File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 554, in getConnection\n compress = dbinfo.compress,\n File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/__init__.py", line 81, in Connect\n return Connection(*args, **kwargs)\n File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/connections.py", line 187, in __init__\n super(Connection, self).__init__(*args, **kwargs2)\nOperationalError: (2002, "Can\'t connect to local MySQL server through socket \'/var/run/mysqld/mysqld.sock\' (2)")\n',
'host': 'localhost',
'passwd': u'plurk',
'port': 3306,
'refresh': True,
'success': False,
'use_unicode': True,
'user': u'plurk'},
{'charset': 'utf8',
'compress': False,
'exception': 'Traceback (most recent call last):\n File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 554, in getConnection\n compress = dbinfo.compress,\n File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/__init__.py", line 81, in Connect\n return Connection(*args, **kwargs)\n File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/connections.py", line 187, in __init__\n super(Connection, self).__init__(*args, **kwargs2)\nOperationalError: (2002, "Can\'t connect to local MySQL server through socket \'/var/run/mysqld/mysqld.sock\' (2)")\n',
'host': 'localhost',
'passwd': u'plurk',
'port': 3306,
'refresh': True,
'success': False,
'use_unicode': True,
'user': u'plurk'},
{'charset': 'utf8',
'compress': False,
'exception': 'Traceback (most recent call last):\n File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 554, in getConnection\n compress = dbinfo.compress,\n File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/__init__.py", line 81, in Connect\n return Connection(*args, **kwargs)\n File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/connections.py", line 187, in __init__\n super(Connection, self).__init__(*args, **kwargs2)\nOperationalError: (2002, "Can\'t connect to local MySQL server through socket \'/var/run/mysqld/mysqld.sock\' (2)")\n',
'host': 'localhost',
'passwd': u'plurk',
'port': 3306,
'refresh': True,
'success': False,
'use_unicode': True,
'user': u'plurk'}]
: {'charset': 'utf8', 'use_unicode': True, 'server_name': 'plurk_group005', 'db': '', '_rhost': u'192.168.0.18', 'compress': False, 'resolve_host': , 'shard_info': {'name': u'mothra', 'id': 2L, 'host': u'192.168.0.18', 'host_extra': u'192.168.0.16', 'balancing': u'host', 'user': u'plurk', 'password': u'plurk', 'port': 3306}, 'port': 3306, 'host': 'localhost', 'user': '*', 'refresh_host': , 'password': '*', 'id': 'shard_db_:2'}.
Error was Traceback (most recent call last):
File "/home/plurk/plurk/git_trunk/ext/parts/db/wrapper.py", line 554, in getConnection
compress = dbinfo.compress,
File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/__init__.py", line 81, in Connect
return Connection(*args, **kwargs)
File "/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.3-py2.6-linux-x86_64.egg/MySQLdb/connections.py", line 187, in __init__
super(Connection, self).__init__(*args, **kwargs2)
OperationalError: (2002, "Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)")

這告訴我們一件事情,那就是不管你的程式技術有多好,對自己的功力多有信心,有些基礎的工夫一定還是要做的,那就是Error Handling,特別是對一般用戶的Error Handling。如果真的跑出來的話,千萬不要跑得太詳細,像是上面這個例子。

第一,對一般沒有技術背景的用戶而言,出現這樣的錯誤訊息可能會讓他們害怕,不知所措,甚至還有的用戶會懷疑是不是中毒了,甚至是打擊對這個網站的信心。

第二,對有技術背景的用戶而言,這居然吐出了Plurk的資料庫帳號跟密碼,還有連線位置,不過看這個資料庫的連線路徑是localhost,表示這是連接到本機端的,剛剛telnet了一下www.plurk.com 3306,沒有辦法連上,應該防火牆把對外的3306 port擋掉了。另外,由於Python的Trackback實在太詳細了,詳細到把Plurk所有Call stack跟程式碼實體路徑的位置,使用的Library全部都告訴人家。如果不是有防火牆把3306擋住,又沒有把plurk的帳號權限設定好的話,後果不堪設想。

不過,另我比較訝異的有幾點,首先是資料庫的連線位置,居然是localhost,這實在太奇怪了,像是Plurk這種流量的服務,居然可以使用在本機端的資料庫,或者,Plurk使用MySql的用途其實只會產生很小的Loading?

另外在Production的機器上用plurk/plurk這種超好猜的密碼也令人非常意外…難道是認為防火牆Rule可以擋住一切?

這幾點讓人對於Plurk的安全性產生質疑…Plurk運作這麼久了出現這個問題,令人有點訝異。不過,這是一個好案例,永遠不要相近自己程式設計的Try Except可以解決一切問題,在程式之上還要有一個底層的Error Handler。