智慧城市真的安全吗?看看这款APP的分析报告

简介: 本文讲的是智慧城市真的安全吗?看看这款APP的分析报告,今年早些时候,我收到了我们市政府的一个NextDoor讯息,声称他们为民众提供了(购买)一个智慧城市应用程序。在此之前并没有任何关于这个应用程序的信息,而我也没有在网上找到其相关的内容,所以对于它我非常有兴趣尽兴更多地了解。
本文讲的是 智慧城市真的安全吗?看看这款APP的分析报告今年早些时候,我收到了我们市政府的一个NextDoor讯息,声称他们为民众提供了(购买)一个智慧城市应用程序。在此之前并没有任何关于这个应用程序的信息,而我也没有在网上找到其相关的内容,所以对于它我非常有兴趣尽兴更多地了解。

智慧城市真的安全吗?看看这款APP的分析报告

根据应用程序的描述,Bright City是“为城市和执法机构面向民众提供服务的专用移动应用程序”。除了NextDoor公告中提到的“财产锁箱”功能外,该应用还支持接收和提交报告可疑活动,当民众离开家之后进行财产监控保护,保护公民的家,并可报告市政维护问题。Bright City的网站上还介绍了一些额外的功能,包括公民可以去了解市政费用的去向,许可证等内容。

智慧城市真的安全吗?看看这款APP的分析报告

而我们可以看到应用程序中涉及的信息的性质似乎非常敏感,所以我很有兴趣进行仔细观察。我下载安装了应用程序,创建了一个帐户并登录。这是初始面板:

智慧城市真的安全吗?看看这款APP的分析报告

接下来我开始浏览应用中的某些选项,以生成一些API流量,在这一过程中没有发现到一些非常严重的问题 。下面是应用程序在获取当前用户的配置文件信息时所做的请求示例:

GET https://api.brightcityapps.com/api/user/getuser/***REMOVED*** HTTP/1.1
Host: api.brightcityapps.com
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Linux; Android 7.1.2; Pixel XL Build/NHG47L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en-US
X-Requested-With: com.mobilesciencetech.brightcity

这里和我最近写的另一个本地市政应用程序类似,该请求不需要任何身份验证。毫无疑问这是令人震惊的,因为这会使一切变得更糟。以下是对上述请求的API响应:

{
"BusinessWatches": [],
"Carrier": null,
"City": null,
"CityGroups": [],
"CityGroupPermissions": [],
"DepartmentCallDetails": [],
"EventSignUps": [],
"HomeWatches": [],
"Lockboxes": [],
"Lockboxes1": [],
"LockboxInsurances": [],
"Lookouts": [],
"LookoutInfoes": [],
"Maintenances": [],
"OpenRecords": [],
"PatrolRequests": [],
"Permissions": [],
"Photo": null,
"Supports": [],
"SurveyResponses": [],
"UserNotificationSettings": [],
"UserLogins": [],
"UsersDevices": [],
"UserTransactions": [],
"UtilityBills": [],
"id": ***REMOVED***,
"cityid": 17,
"first": "Randy",
"mi": null,
"last": "Westergren",
"phone": null,
"cell": "***REMOVED***",
"carrierid": null,
"email": "***REMOVED***",
"dob": null,
"username": "rwestergren",
"password": "***REMOVED***",
"thumb": null,
"created": "2017-05-21T16:57:00",
"active": 1,
"admin": 0,
"brainTreeCustomerId": null
}

请注意,虽然我从上面的输出中删除了它,但用户的密码是以纯文本形式返回的。显然,这一点也非常的糟糕。

接下来,我决定反编译应用程序,看看我是否可以找到更详尽的API端点列表(以及是否需要任何身份验证)。这是过程中主要的活动:

package com.mobilesciencetech.brightcity;
 
import android.content.Intent;
import android.os.Bundle;
import org.apache.cordova.CordovaActivity;
 
public class BrightCity extends CordovaActivity
{
 
    public BrightCity()
    {
    }
 
    public void onCreate(Bundle bundle)
    {
        super.onCreate(bundle);
        bundle = getIntent().getExtras();
        if(bundle != null && bundle.getBoolean("cdvStartInBackground", false))
            moveTaskToBack(true);
        loadUrl(launchUrl);
    }
}

由于该应用程序使用Android Cordova,因此几乎所有代码都不在Java中。相反,当我转移到 assets / www  目录时,我发现了所有使应用程序运行的HTML / JS。

智慧城市真的安全吗?看看这款APP的分析报告

我打开了 editlockbox.js 文件,并查看了一下包含一些API请求的JavaScript:

$.getJSON(url + '/api/LockboxInsurance/GetLockboxInsuranceCompanies', function(companiesJsonPayload) {
    $('#companyid').append("<option value='0'>Select Insurance Company</option>");
    $(companiesJsonPayload).each(function(i, item) {
        $('#companyid').append('<option value="' + item.id + '">' + item.name + '</option>');
    });
    $('#companyid').append("<option value='Add'>Add New Company</option>");
});
 
var userid = window.localStorage.getItem("userid");
$("#itemid").val(id);
$("#userid").val(window.localStorage.getItem("userid"));
 
$.getJSON(url + '/api/lockbox/getlockbox/' + id + '', function(result) {
    if (result == null) {
        $('#result').append('<li>Item does not exist</li>');
    } else {
        $("#userid").val(window.localStorage.getItem("userid"));
        $("#name").val(result.name);
        $.getJSON(url + '/api/lockboxcategory/getlockboxcategories', function(categoriesJsonPayload) {
            $(categoriesJsonPayload).each(function(i, item) {
                if (item.id == result.categoryid) {
                    $('#categoryid').append('<option value="' + item.id + '" selected>' + item.name + '</option>');
                } else {
                    $('#categoryid').append('<option value="' + item.id + '">' + item.name + '</option>');
                }
            });
        });
        $.getJSON(url + '/api/LockboxInsurance/GetUserPolicies/' + userid + '', function(companiesJsonPayload) {
            $('#insurance').append("<option value='0'>Select an insurance policy (choose one)</option>");
            $(companiesJsonPayload).each(function(i, item) {
                if (item.PolicyID == result.policyid) {
                    $('#insurance').append('<option value="' + item.PolicyID + '" selected>' + item.CompanyName + "-" + item.PolicyNumber + '</option>');
                } else {
                    $('#insurance').append('<option value="' + item.PolicyID + '">' + item.CompanyName + "-" + item.PolicyNumber + '</option>');
                }
                //$('#insurance').append('<option value="' + item.PolicyID + '">' + item.CompanyName + "-" + item.PolicyNumber + '</option>');
            });
            $('#insurance').append("<option value='Add'>Add new policy</option>");
        });
        $("#description").val(result.description);
        $("#serial").val(result.serial);
        $("#make").val(result.make);
        $("#model").val(result.model);
        $("#caliber").val(result.caliber);
        $("#additionalinfo").val(result.additionalinfo);
        $("#room").val(result.roomlocation);
        $.getJSON(url + '/api/lockbox/GetUserLocationRooms/' + userid + '', function(companiesJsonPayload) {
            $('#room').append("<option value='0'>Select location of item (choose one)</option>");
            $(companiesJsonPayload).each(function(i, item) {
                if (item.Name == result.roomlocation) {
                    $('#room').append('<option selected value="' + item.Name + '">' + item.Name + '</option>');
                } else {
                    $('#room').append('<option value="' + item.Name + '">' + item.Name + '</option>');
                }
            });
            $('#room').append("<option value='Add'>Add new location</option>");
        });
        //$("#insurance").val(result.policyid);
        $.getJSON(url + '/api/photo/getphoto/' + id, function(photoresult) {
            if (photoresult.name != "") {
                var image = document.getElementById('thumb');
                var imageData = iurl + '/upload/lockbox/' + id + '/thumb/' + photoresult.name;
                image.src = imageData;
                image.style.display = 'block';
                //$('#showthumb').html('<img src="' + iurl + '/upload/lockbox/' + id + '/thumb/' + photoresult.name + '" style="width:56px; height:56px; border-radius: 28px; -webkit-border-radius: 28px; -moz-border-radius: 28px;" />');
            }
        });
        $.getJSON(url + '/api/photo/getdoc/' + id, function(docresult) {
            if (docresult != null) {
                alert(docresult.name);
                var doc = document.getElementById('doc');
                var docData = iurl + '/upload/lockbox/' + id + '/doc/' + docresult.name;
                doc.src = docData;
                doc.style.display = 'block';
                //$('#showdoc').html('<a href="' + iurl + '/upload/lockbox/' + id + '/doc/' + docresult.name + '" style="width:56px; height:56px; border-radius: 28px; -webkit-border-radius: 28px; -moz-border-radius: 28px;">' + docresult.name + '</a>');
            }
        });
        //$.getJSON(url + '/api/lockboxinsurance/GetInsuranceDetails/' + id, function (insresult) {
        //    if (insresult != null) {
        //        $("#policynumber").val(insresult.PolicyNumber);
        //        $("#companyid").val(insresult.CompanyID);
        //    }
        //});
    }
});

您可以看到,所有请求都以类似的方式进行,没有任何身份验证或会话状态机制。接下来,我写了一个快速脚本来搜索所有的JavaScript文件,以生成所有端点的列表:

/api/agency/
/api/agency/getagency/
/api/brightcityapp/
/api/brightcityapp/geteventdetails/
/api/brightcityapp/geteventsforagency/
/api/brightcityapp/geteventsforagencybydaterange/
/api/brightcityapp/geteventsforagencybyloadcount/
/api/brightcityapp/geteventsforagencynew/
/api/brightcityapp/geteventsignupdetails/
/api/brightcitypayments/getcitypaymentsbydaterangenew/
/api/brightcitypayments/getcitypaymentsbyloadcountnew/
/api/brightcitypayments/getcitypaymentsnew/
/api/brightcitypayments/getcityutilpaymentsnew/
/api/brightcitypayments/geteventpaymentdetails/
/api/brightcitypayments/geteventpaymentsbydaterangenew/
/api/brightcitypayments/geteventpaymentsbyloadcountnew/
/api/brightcitypayments/geteventpaymentsnew/
/api/brightcitypayments/geteventutilpaymentsnew/
/api/brightcitypayments/getpaymentdetails/
/api/business/getbusinessesbyagency/
/api/businesswatch/
/api/businesswatch/cancelbusinesswatch/
/api/businesswatch/getbusinesswatch/
/api/businesswatch/getbusinesswatchesforagencybydaterangenew/
/api/businesswatch/getbusinesswatchesforagencybyloadcountnew/
/api/businesswatch/getbusinesswatchesforagencynew/
/api/businesswatchstatus/
/api/businesswatchstatus/getbusinesswatchupdates/
/api/city/getcitiesbyagency/
/api/country/
/api/eyecolor/
/api/gender/
/api/glass/
/api/haircolor/
/api/height/
/api/homewatch/
/api/homewatch/cancel/
/api/homewatch/cancelhomewatch/
/api/homewatch/gethomewatch/
/api/homewatch/gethomewatchesforagencybydaterangenew/
/api/homewatch/gethomewatchesforagencybyloadcountnew/
/api/homewatch/gethomewatchesforagencynew/
/api/homewatchstatus/
/api/homewatchstatus/gethomewatchupdates/
/api/house/gethousesbyagency/
/api/lockbox/
/api/lockbox/deletelockbox/
/api/lockbox/getlockbox/
/api/lockbox/getlockboxesforagencybydaterangenew/
/api/lockbox/getlockboxesforagencybyloadcountnew/
/api/lockbox/getlockboxesforagencynew/
/api/lockboxcategory/
/api/lockboxcategory/getlockboxcategory/
/api/lookout/
/api/lookout/getlookout/
/api/lookout/getlookoutsforagencybydaterangenew/
/api/lookout/getlookoutsforagencybyloadcountnew/
/api/lookout/getlookoutsforagencynew/
/api/lookoutinfo/
/api/lookoutinfo/getlookoutinfoforlookout/
/api/maintenance/
/api/maintenance/cancel/
/api/maintenance/getmaintenance/
/api/maintenance/getmaintenanceforagencybydaterangenew/
/api/maintenance/getmaintenanceforagencybyloadcountnew/
/api/maintenance/getmaintenanceforagencynew/
/api/maintenance/getpublicmaintenanceforagencybydaterangenew/
/api/maintenance/getpublicmaintenanceforagencybyloadcountnew/
/api/maintenance/getpublicmaintenanceforagencynew/
/api/maintenancestatus/
/api/maintenancestatus/getmaintenancestatusforrequest/
/api/message/
/api/message/getmessage/
/api/message/getmessagesforofficerbydaterangenew/
/api/message/getmessagesforofficerbyloadcountnew/
/api/message/getmessagesforofficernew/
/api/newsfeed/getagencynewsfeedslist/
/api/officer/
/api/officer/getofficer/
/api/openrecord/acceptopenrecordrequest/
/api/openrecord/getopenrecord/
/api/openrecord/getopenrecordsforagency/
/api/openrecord/getopenrecordsforagencybydaterange/
/api/openrecord/getopenrecordsforagencybyloadcount/
/api/patrolrequest/acceptpatrolrequest/
/api/patrolrequest/getpatrolrequest/
/api/patrolrequest/getpatrolrequestsforagencybydaterangenew/
/api/patrolrequest/getpatrolrequestsforagencybyloadcountnew/
/api/patrolrequest/getpatrolrequestsforagencynew/
/api/photo/getdoc/
/api/photo/getmaintenancephoto/
/api/photo/getphoto/
/api/race/
/api/scamalert/
/api/scamalert/deletescamalert/
/api/scamalert/getscamalert/
/api/scamalert/getscamalertsforagencybydaterangenew/
/api/scamalert/getscamalertsforagencybyloadcountnew/
/api/scamalert/getscamalertsforagencynew/
/api/skintone/
/api/state/
/api/state/getstate/
/api/support/
/api/trafficalert/
/api/trafficalert/deletetrafficalert/
/api/trafficalert/gettrafficalert/
/api/trafficalert/gettrafficalertsforagencybydaterangenew/
/api/trafficalert/gettrafficalertsforagencybyloadcountnew/
/api/trafficalert/gettrafficalertsforagencynew/
/api/user/
/api/user/getuser/
/api/weatheralert/
/api/weatheralert/deleteweatheralert/
/api/weatheralert/getweatheralert/
/api/weatheralert/getweatheralertsforagencybydaterangenew/
/api/weatheralert/getweatheralertsforagencybyloadcountnew/
/api/weatheralert/getweatheralertsforagencynew/
/api/weight/

此外,我在我的帐户下设置了一个“锁箱”,并上传了一些图像来测试其功能。而对于我的目录列表被允许查看这一点我没有丝毫的意外,这意味着应用程序中所有上传的文档和图像都可以被公开访问。

风险

毫无疑问,使用此应用程序的公众面临的风险非常多并且十分严重。应用程序本身存储的敏感信息,攻击者可以轻易获得,进而可以以其他用户(或警察机构)的身份向系统内提交欺诈性的活动和事件。

没有基本的认证要求,任何应用程序信息或操作/事件的完整性都不能保证是合法的。必须要清楚的一点是,这个应用程序中包含有用户密码(和其他个人信息),可疑人员的常驻报告,公民电子目录,甚至本系统中存储和使用的付款信息,而这些都是不安全的。 

同样需要注意的是,其对于用户的影响可能远远超过Bright City系统。用户在多个系统中使用相同的密码是常见的,因此以明文形式显示用户密码可能导致其他帐户(例如电子邮件,银行,社交媒体)的一一告破。 

报告

目前我已经就如何以最有效的方式解决这些问题进行了多次的推敲理论。在我的其他博客文章中,我总是尽力与供应商合作,报告和修补已识别的漏洞 – 但这次有点不同。我积极的向市政府报告了上述问题,我们有一个简短的通话来对此进行讨论。尽管供应商最终将整个应用程序脱机以实现身份验证,但我报告的其他许多问题仍未解决(包括目录列表问题)。

编者:在各类智慧城市、智慧社区等概念大举进入我们的生活中时,我们仍然需要时时刻刻的去关注安全问题,智慧化智能化的设备、程序固然能够使我们的生活更为便捷,但安全出了问题也可能让我们的生活陷入一片黑暗。




原文发布时间为:2017年7月31日
本文作者:鲁班七号
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。
目录
相关文章
|
3月前
|
安全 Java 应用服务中间件
【Azure 应用服务】App Service 默认页面暴露Tomcat版本信息,存在安全风险
【Azure 应用服务】App Service 默认页面暴露Tomcat版本信息,存在安全风险
|
8天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
1月前
|
安全 网络安全 Android开发
深度解析:利用Universal Links与Android App Links实现无缝网页至应用跳转的安全考量
【10月更文挑战第2天】在移动互联网时代,用户经常需要从网页无缝跳转到移动应用中。这种跳转不仅需要提供流畅的用户体验,还要确保安全性。本文将深入探讨如何利用Universal Links(仅限于iOS)和Android App Links技术实现这一目标,并分析其安全性。
224 0
|
1月前
|
监控 安全 Apache
构建安全的URL重定向策略:确保从Web到App平滑过渡的最佳实践
【10月更文挑战第2天】URL重定向是Web开发中常见的操作,它允许服务器根据请求的URL将用户重定向到另一个URL。然而,如果重定向过程没有得到妥善处理,可能会导致安全漏洞,如开放重定向攻击。因此,确保重定向过程的安全性至关重要。
69 0
|
2月前
|
安全
【Azure App Service】App service无法使用的情况分析
App Service集成子网后,如果子网网段中的剩余IP地址非常少的情况下,会在App Service实例升级时( 先加入新实例,然后在移除老实例 )。新加入的实例不能被分配到正确的内网IP地址,无法成功的访问内网资源。 解决方法就是为App Service增加子网地址, 最少需要/26 子网网段地址。
|
3月前
【Azure Function App】本地运行的Function发布到Azure上无法运行的错误分析
【Azure Function App】本地运行的Function发布到Azure上无法运行的错误分析
|
3月前
|
开发框架 缓存 .NET
【App Service】在Azure App Service中分析.NET应用程序的性能的好帮手(Review Stack Traces)
【App Service】在Azure App Service中分析.NET应用程序的性能的好帮手(Review Stack Traces)
|
3月前
|
C# 开发工具
【Azure 应用服务】Azure Function App使用SendGrid发送邮件遇见异常消息The operation was canceled,分析源码渐入最源端
【Azure 应用服务】Azure Function App使用SendGrid发送邮件遇见异常消息The operation was canceled,分析源码渐入最源端
|
3月前
|
网络协议 NoSQL 网络安全
【Azure 应用服务】由Web App“无法连接数据库”而逐步分析到解析内网地址的办法(SQL和Redis开启private endpoint,只能通过内网访问,无法从公网访问的情况下)
【Azure 应用服务】由Web App“无法连接数据库”而逐步分析到解析内网地址的办法(SQL和Redis开启private endpoint,只能通过内网访问,无法从公网访问的情况下)
|
5月前
|
移动开发 小程序 开发工具
微信支付的类型分析(JSAPI+APP+H5+NATIVE+付款码+合单)
微信支付的类型分析(JSAPI+APP+H5+NATIVE+付款码+合单)
539 1

热门文章

最新文章