Программное средство Инвентаризация компьютеров. ПЗ_new. Руководство пользователя для разработанной системы. Пятый раздел приводит данные по информационной безопасности приложения
Скачать 4.8 Mb.
|
6.4 Межсайтовая подделка запросов Это такой тип атаки, при которой пользователь заходит на какой-либо вредоносный сайт, и этот сайт отправляет запросы на хороший сайт, на котором пользователь зарегистрирован, и который он недавно посещал. Может случится такое, что информации об авторизации на хорошем сайте все еще остается в куки. Тогда вполне может быть совершено и какое-то вредоносное скрытое действие. Атака может быть реализована путем включения ссылки или сценария в веб-страницу, с которой происходит переход на вредоносный сайт, считающий, что пользователь прошел авторизацию. Далее злоумышленник может получить авторизационные куки хорошего сайта и с ними отправить ему запрос, а тот проверив куки ничего не заподозрит. Риску подвергаются веб-приложения, которые выполняют действия, основанные на данных от доверяемых или авторизованных пользователей без требования от пользователя подтвердить конкретное действие. Пользователь, который авторизуется с помощью куки его веб-браузера может невольно направить HTTP-запрос на сайт, который доверяет этому пользователю, вызвав тем самым нежелательные действия. 6.5 Вывод по разделу В разделе было уделено внимание анализу информационной безопасности разработанного кроссплатформенного приложения и некоторым методам защиты от уязвимостей. При разработке обязательно следует учитывать аспекты безопасности и проанализировать уязвимости, через которые возможна реализация угроз, и которые действуют на веб-приложение в виде отказов или снижения его работоспособности. Самые популярные виды атак и способы защиты от них разработанного веб-приложения были рассмотрены. В случае с личными данными пользователя не нужно беспокоиться о их защите, так как защиту предоставляет Firebase. Таким образом, можно сделать вывод о том, что личные данные пользователя максимально защищены в разработанном кроссплатформенном приложении и при его использовании ему не угрожает опасность вредоносных атак злоумышленников. Заключение В рамках дипломного проекта был разработан Интернет-сервис для инвентаризации компьютерного и офисного оборудования кафедры вуза. Для достижения поставленной цели решены следующие задачи: проведен анализ аналогичных решений, выявлены их преимущества и недостатки; определены основные функции разрабатываемого интернет-сервиса; определены наиболее подходящие технологии для создания интернет-сервиса; разработна серверная часть, включающая в себя основную логику осуществляющую хранение данных различных рабочих процессов в базе данных, обработку данных и использование; проведено тестирование интернет-сервиса; выполнено экономическое обоснование разработки. В результате проделанной работы разработан сервис, представляющий из себя сервер и кроссплатформенное клиентское приложение. Для достижения поставленной цели были реализованы следующие задачи: разработана архитектура программного средства; реализовано наличие нескольких ролей в программном средстве; настроены сервисы HotChocolate и IdentityServer; подключены сервисы и пакеты для выполнения поставленных задач. Так же было проведено экономическое обоснование. Результаты соответствуют заявленным требованиям. Проверена работоспособность модулей приложения и их связи с удаленными сервисами. Проведена проверка качества кода каждого компонента в соответствии с современными стандартами качества и рекомендациями к ним. Данное приложение будет интересно пользователям, желающим сэкономить собественное время, на ведении бухгалтерного учёта. Список использованных источников 1 Наполи, Марко Л. Beginning Flutter: A Hands On Guide to App Development [Текст] – Wiley, 2019 – 453 c. – Дата доступа: 15.03.2020 2 Firebase docs [Электронный ресурс] / электронный документ – Режим доступа: www.firebase.doc.org – Дата доступа: 02.04.2020 3 Виндмилл, Э. Flutter in Action [Текст] – Manning, 2017 – 97 c. – Дата доступа 17.03.2020 4 pub.dev [Электронный ресурс]: документация. – Режим доступа: www.pub.dev/documentation/ – Дата доступа: 05.03.2020. 5 Замметти, Ф Practical Flutter [Текст] – Apress, 2019 – 295 c. – Дата доступа 07.03.2020 6 Заккагино, К. Programming Flutter [Текст] – Pargprog, 2019 – 95 c. – Дата доступа: 15.03.2020 7 Cross Site Scripting (XSS) [Электронный ресурс] / Owasp. – Режим доступа: https://owasp.org/www-community/attacks/xss/. – Дата доступа: 01.05.2020. 8 Каштелян Т. В. Экономическое обоснование дипломных проектов/ Каштелян Т.В. – Минск: УО «Белорусский государственный технологический университет», 2012. – 88 с. 9 Формирование цены разработки проекта [Электронный ресурс] / электронный документ – Режим доступа: https://it-hod.by/price#softdevelopment – Дата доступа: 08.03.2020 10 Список стоимостей разработки мобильного приложения [Электронный ресурс] / электронный – Режим доступа: https://workspace.ru/minsk/mobile/ – Дата доступа: 08.03.2018 ПРИЛОЖЕНИЕ А Исходный код auth_bloc.dart import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; part 'auth_event.dart'; part 'auth_state.dart'; class AuthBloc extends Bloc @override AuthState get initialState => AuthLoginState(); @override Stream AuthEvent event, ) async* { if (event is AuthLoginEvent) { yield AuthLoginState(); } else if (event is AuthRegisterEvent) { yield AuthRegisterState(); } else if (event is AuthStaffLoginEvent) { yield AuthStaffLoginState(); } } } ПРИЛОЖЕНИЕ Б Исходный код tracking_page.dart import 'dart:async'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; class TrackingPage extends StatefulWidget { final DocumentSnapshot d; final String orderID; final bool client; TrackingPage({Key key, this.d, this.orderID, this.client = true}) : super(key: key); @override _TrackingPageState createState() => _TrackingPageState(); } class _TrackingPageState extends State Completer LatLng location; bool staff = false; Timer trackUser; Timer trackCourier; Marker courierMarker; Stream Set Marker toDeliver; void getCurrentPosition(BuildContext ctx) async { Geolocator g = Provider.of Position p = await g.getCurrentPosition(desiredAccuracy: LocationAccuracy.best); setState(() { location = LatLng(p.latitude, p.longitude); }); } void loadPreferences() async { SharedPreferences prefs = await SharedPreferences.getInstance(); staff = prefs.getBool('staff'); } @override Widget build(BuildContext context) { loadPreferences(); getCurrentPosition(context); toDeliver = Marker( markerId: MarkerId('client'), position: LatLng(widget.d.data["deliverTo"].latitude, widget.d.data["deliverTo"].longitude), ); markers.add(toDeliver); if (widget.client) { courierStream = Firestore.instance.document("orders/${widget.orderID}").snapshots(); courierStream.listen((event) { courierMarker = Marker( markerId: MarkerId("courier"), position: LatLng(event.data["lastLocation"].latitude, event.data["lastLocation"].longitude), infoWindow: InfoWindow(title: 'Courier Location'), icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue)); markers.clear(); markers.add(toDeliver); markers.add(courierMarker); }); } return Scaffold( appBar: AppBar(), body: Container( child: Column( children: [ Expanded( flex: 16, child: location != null && (courierMarker != null || widget.client == false) ? GoogleMap( mapType: MapType.terrain, initialCameraPosition: CameraPosition( bearing: 0, target: location, tilt: 0, zoom: 15, ), onMapCreated: (controller) => _controller.complete(controller), markers: markers, ) : Center( child: CircularProgressIndicator(), ), ), Expanded( flex: widget.client ? 0 : 2, child: Container( child: widget.client ? null : Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ RaisedButton( child: Text('Start tracking'), onPressed: trackUser != null ? null : () { trackUser = Timer.periodic(Duration(seconds: 15), (timer) { Geolocator() .getCurrentPosition( desiredAccuracy: LocationAccuracy.best, ) .then((v) { courierMarker = Marker( markerId: MarkerId("courier"), position: LatLng(v.latitude, v.longitude), infoWindow: InfoWindow(title: 'Courier Location'), icon: BitmapDescriptor.defaultMarkerWithHue( BitmapDescriptor.hueBlue)); markers.clear(); markers.add(toDeliver); markers.add(courierMarker); Firestore.instance .document("orders/${widget.orderID}") .updateData({ 'lastLocation': GeoPoint(v.latitude, v.longitude) }); }); }); }, ), RaisedButton( child: Text('Stop tracking'), onPressed: trackUser != null ? () { trackUser.cancel(); trackUser = null; } : null, ), RaisedButton( child: Text('Finish Delivery'), onPressed: () { Firestore.instance.document("orders/${widget.orderID}").updateData({ 'delivered': true });},)],),),)],),),);}} ПРИЛОЖЕНИЕ В Исходный код main.dart import 'package:auto_route/auto_route.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:meal_kit_delivery/bloc/auth_bloc.dart'; import 'package:meal_kit_delivery/routes/router.gr.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ Provider Provider Provider Provider Provider BlocProvider ], child: MaterialApp( debugShowCheckedModeBanner: false, title: 'Material App', theme: ThemeData.light().copyWith( primaryColor: Colors.deepOrange[700], accentColor: Colors.deepOrangeAccent, ), builder: ExtendedNavigator ),);}} ПРИЛОЖЕНИЕ Г Исходный код paymet_page.dart import 'package:auto_route/auto_route.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:intl/intl.dart'; import 'package:meal_kit_delivery/models/meal_model.dart'; import 'package:meal_kit_delivery/models/ingredients_model.dart'; import 'package:provider/provider.dart'; class PaymentPage extends StatefulWidget { final int count; final Meal meal; final Ingredients ingredients; PaymentPage({Key key, this.count, this.meal, this.ingredients}) : super(key: key); @override _PaymentPageState createState() => _PaymentPageState(); } class _PaymentPageState extends State { FirebaseUser current; @override void initState() { FirebaseAuth fba = FirebaseAuth.instance; fba.currentUser().then((value) => setState(() { current = value; })); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Container( child: current == null ? Center( child: CircularProgressIndicator(), ) : Column( children: [ Expanded( flex: 9, child: Container( padding: EdgeInsets.all(16), child: Container( decoration: BoxDecoration( border: Border.all( color: Colors.black, width: 3, ), ), padding: EdgeInsets.all(16), child: Column( children: [ Expanded( flex: 1, child: Row( children: [ Icon( Icons.shopping_cart, size: 30, ), Text( 'Order Summary', style: TextStyle(fontSize: 18), )],),), Expanded( flex: 1, child: Row( children: [ Text( 'Items in order: ${widget.count}', style: TextStyle(fontSize: 16), ) ], ), ), Expanded( flex: 6, child: Container( child: ListView.separated( itemCount: widget.count, separatorBuilder: (_, idx) => SizedBox(height: 5), itemBuilder: (_, idx) => Card( child: ListTile( title: Text(widget.meal.name), subtitle: Text("Total mass: ${[ ...widget.ingredients.grain.entries.map( (e) => int.parse( e.value.split(' ')[0])), ...widget.ingredients.spice.entries.map( (e) => int.parse( e.value.split(' ')[0])), ...widget.ingredients.fruit.entries.map( (e) => int.parse( e.value.split(' ')[0])), ...widget.ingredients.vegetable.entries .map((e) => int.parse( e.value.split(' ')[0])), ...widget.ingredients.meat.entries.map( (e) => int.parse( e.value.split(' ')[0])) ].reduce((n, next) => n + next)} g"), trailing: Text("${widget.meal.cost} BYN"), ),),),),), Expanded( flex: 1, child: Container( child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Text( "Sub total: ${NumberFormat('###.##', "en_US").format(widget.meal.cost * widget.count)} BYN", style: TextStyle(fontSize: 24), )],),),)],),),),), Expanded( flex: 5, child: Row( children: [ Expanded( flex: 5, child: Container( child: Column( children: [ Text( 'Order to your position', style: TextStyle(fontSize: 16), ), Padding( padding: const EdgeInsets.only(top: 16.0), child: RaisedButton( child: Text('Order'), color: Theme.of(context).accentColor, onPressed: () { Geolocator g = Provider.of context, listen: false); Firestore fs = Provider.of context, listen: false); g.getCurrentPosition().then((p) { fs.collection("orders").add({ 'count': widget.count, 'meal': widget.meal.documentID, 'user': current.uid, 'deliverTo': GeoPoint(p.latitude, p.longitude), 'total': widget.count * widget.meal.cost, 'delivered': false, 'taken': false }); }); ExtendedNavigator.of(context).pop(); },),)],),),), Expanded( flex: 7, child: Container( child: Column( children: [ Expanded( flex: 1, child: Text( 'Or select from existing', style: TextStyle(fontSize: 16), )), Expanded( flex: 4, child: StreamBuilder( stream: Provider.of .collection("addresses") .where("user", isEqualTo: current.uid) .snapshots(), builder: (ctx, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return Center( child: CircularProgressIndicator(), ); default: if (snapshot.hasData && !snapshot .data.documents.isEmpty) { return ListView.builder( itemCount: snapshot .data .documents .first .data["addresses"] .length, itemBuilder: (_, idx) => ListTile( title: Text(snapshot .data .documents .first .data["addresses"][idx]), trailing: Icon(Icons.location_on), onTap: () { Firestore fs = Provider.of context, listen: false); Provider.of context, listen: false) .placemarkFromAddress( snapshot .data .documents .first .data["addresses"][idx]) .then((value) { fs .collection("orders") .add({ 'count': widget.count, 'meal': widget .meal.documentID, 'user': current.uid, 'deliverTo': GeoPoint( value.first.position .latitude, value.first.position .longitude), 'total': widget.count * widget.meal.cost, 'delivered': false, 'taken': false }); ExtendedNavigator.of( context) .pop(); });}),); } else { return Center( child: Text("No addresses"), );}}},),)],),),)],),)],),),);}} ПРИЛОЖЕНИЕ Д Исходный код addresses_page.dart import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:meal_kit_delivery/elements/side_drawer.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; class AddressesPage extends StatefulWidget { AddressesPage({Key key}) : super(key: key); @override _AddressesPageState createState() => _AddressesPageState(); } class _AddressesPageState extends State FirebaseUser current; String addressID; TextEditingController _address = TextEditingController(); @override void initState() { FirebaseAuth fba = FirebaseAuth.instance; fba.currentUser().then((value) => setState(() { current = value; })); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( drawer: SideDrawer(), appBar: AppBar(), body: current == null ? Center( child: CircularProgressIndicator(), ) : Container( padding: EdgeInsets.all(16), child: StreamBuilder( stream: Provider.of .collection("addresses") .where("user", isEqualTo: current.uid) .snapshots(), builder: (_, snap) { switch (snap.connectionState) { case ConnectionState.waiting: return Center( child: CircularProgressIndicator(), ); default: if (snap.hasData && !snap.data.documents.isEmpty) { addressID = snap.data.documents.first.documentID; print(addressID); return ListView.builder( itemCount: snap .data.documents.first.data["addresses"].length, itemBuilder: (_, idx) => ListTile( title: Text(snap .data.documents.first.data["addresses"][idx]), trailing: Icon(Icons.location_on), onLongPress: () => Firestore.instance .document( "addresses/${snap.data.documents.first.documentID}") .updateData({ "addresses": FieldValue.arrayRemove([snap .data.documents.first.data["addresses"][idx]]) }), ), ); } else { Provider.of .collection("addresses") .add({ "user": current.uid, "addresses": [], }).then((ref) => addressID = ref.documentID); return Center(child: Text('No addresses')); } } }, ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), backgroundColor: Theme.of(context).accentColor, onPressed: () => showDialog( context: context, barrierDismissible: true, builder: (ctx) => AlertDialog( contentPadding: EdgeInsets.all(8), content: Row( children: [ Expanded( child: TextField( controller: _address, autofocus: true, decoration: InputDecoration(labelText: 'Address'), ), ) ], ), actions: [ FlatButton( child: Text('Dismiss'), onPressed: () => Navigator.pop(ctx), textColor: Theme.of(context).primaryColor, ), FlatButton( child: Text('Accept'), textColor: Theme.of(context).primaryColor, onPressed: () { Provider.of .document("addresses/$addressID") .updateData({ "addresses": FieldValue.arrayUnion([_address.text]) }); _address.text = ''; Navigator.pop(ctx); },)],),),),);}} ПРИЛОЖЕНИЕ Е Исходный код deliveries_page.dart import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:meal_kit_delivery/elements/side_drawer.dart'; import 'package:meal_kit_delivery/elements/delivery_item.dart'; import 'package:meal_kit_delivery/elements/order_item.dart'; import 'package:provider/provider.dart'; class DeliveriesPage extends StatefulWidget { DeliveriesPage({Key key}) : super(key: key); @override _DeliveriesPageState createState() => _DeliveriesPageState(); } class _DeliveriesPageState extends State FirebaseUser current; @override void initState() { FirebaseAuth fba = FirebaseAuth.instance; fba.currentUser().then((value) => setState(() { current = value; })); super.initState(); } @override Widget build(BuildContext context) { return DefaultTabController( length: 2, child: Scaffold( drawer: SideDrawer(), appBar: AppBar( bottom: TabBar( indicatorColor: Theme.of(context).accentColor, tabs: Tab(text: 'Deliveries'), Tab(text: 'Orders'), ], ), ), body: current == null ? Center( child: CircularProgressIndicator(), ) : Container( padding: EdgeInsets.all(24), child: Center( child: TabBarView( children: StreamBuilder( stream: Provider.of .collection("orders") .where("user", isEqualTo: current.uid) .where("delivered", isEqualTo: false) .snapshots(), builder: (_, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return Center( child: CircularProgressIndicator(), ); default: return ListView.builder( itemCount: snapshot.data.documents.length, itemBuilder: (_, idx) { return DeliveryItem( isTracking: true, d: snapshot.data.documents[idx]); }, ); } }, ), StreamBuilder( stream: Provider.of .collection("orders") .where("user", isEqualTo: current.uid) .where("delivered", isEqualTo: true) .snapshots(), builder: (_, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return Center( child: CircularProgressIndicator(), ); default: return ListView.builder( itemCount: snapshot.data.documents.length, itemBuilder: (_, idx) { return OrderItem( d: snapshot.data.documents[idx]); },);}},),],),),),),);}} ПРИЛОЖЕНИЕ Ж ПРИЛОЖЕНИЕ И ПРИЛОЖЕНИЕ К ПРИЛОЖЕНИЕ Л ПРИЛОЖЕНИЕ М |